BT

Início Artigos Hoverfly: Virtualização de Serviços em Java

Hoverfly: Virtualização de Serviços em Java

Favoritos

Pontos Principais

  • Em uma arquitetura de microservices uma das partes mais importantes de um serviço é o módulo responsável pela comunicação com outros serviços.
  • Frequentemente precisamos testar de ponta-a-ponta como um serviço se comunica com outros. Simular não é uma solução, já que mockar não testa a comunicação por inteiro e pula toda a parte relacionada ao protocolo de rede (ex: HTTP). Executar o serviço dependente também não é uma opção devido ao esforço necessário para preparar o processo a cada execução.
  • Virtualização de serviço é uma técnica utilizada para simular o comportamento das dependências do serviço a partir da criação de um serviço de proxy. Os testes são executados em um serviço (ex: testado de ponta-a-ponta) sem a inicialização do serviço real.
  • O Hoverfly é uma ferramenta de simulação com uma API de virtualização de serviços escrita em GO, leve de código aberto, fortemente integrada com o Java.

Em uma arquitetura de microservices, a aplicação é formada por diversos serviços interconectados no qual todos trabalham em conjunto para produzir uma funcionalidade necessária. Normalmente uma arquitetura de microservices se parece com o modelo abaixo:

Todo serviço depende de outro serviço e alguns deles são suportados por um banco de dados. Cada serviço deve ter o seu próprio conjunto de testes (unitário, componente, contrato, etc) para validar o funcionamento, por exemplo após uma modificação.

Vamos focar em um único serviço que seria um desses no meio do gráfico. Se olharmos dentro dele, poderemos ver algo bem parecido com a imagem a seguir:

Um serviço normalmente contém algumas dessas camadas (ou todas), que resumidamente podem ser descritas como:

  • Recursos: Agem como um ponto de entrada para o serviço. Serializam ou deserializam as mensagens que chegam na forma de qualquer protocolo (ex: JSON) e as transforma em objetos de domínio. Também validam se os parâmetros são válidos e são os responsáveis por transformar objetos de domínio para o protocolo de mensagem e os retorna como resposta da solicitação. Por exemplo se a tecnologia usada é o Jakarta EE/MicroProfile o JAX-RS toma conta de todos esses detalhes, o mesmo vale para o Spring Boot ou para qualquer outra tecnologia;
  • Negócio: Camada na qual todas as regras do negócio do serviço são implementadas, e onde é orquestrado todo o acesso a camada de persistência e Gateway. Objetos de domínio fazem parte dessa camada;
  • Persistência: Parte responsável por persistir os objetos de domínio em um banco de dados (SQL/NoSQL). Se a tecnologia usada é o Jakarta EE/MicroProfile, o JPA é responsável por persistir os dados somente no caso do banco de dados ser SQL;
  • Gateway: De longe todas as partes anteriores são comuns em qualquer outra arquitetura, mas gateways são camadas típicas de arquiteturas distribuídas. O gateway encapsula toda a lógica de comunicação para consumir um determinado serviço, entendendo como serializar e deserializar as respostas do serviço consumido para objetos de domínio, assim como encapsula as configurações de rede como cliente, timeout, tentativas, resiliência. Normalmente quando estivermos utilizando o Jakarta EE/MicroProfile e o JSON como protocolo de mensagens provavelmente estaremos utilizando o JAX-RS como o cliente HTTP para se comunicar com um outro serviço.

Podemos testar as camadas superiores mockando a camada do gateway. Por exemplo, um código utilizando Mockito para testar a camada de negócios pode se parecer com o código abaixo:

   @Mock
   WorldClockServiceGateway worldClockServiceGateway;

   @Test
   public void should_deny_access_if_not_working_hour() {

       // Given

       when(worldClockServiceGateway.getTime("cet")).thenReturn(LocalTime.of(0,0));

       SecurityResource securityResource = new SecurityResource();
       securityResource.worldClockServiceGateway = worldClockServiceGateway;

       // When

       boolean access = securityResource.isAccessAllowed();

       // Then

       assertThat(access).isFalse();
}

Mas com certeza ainda precisamos validar se a classe de gateway (WorldClockServiceGateway) funciona como o esperado:

  • Validar se é capaz de serializar ou deserializar um protocolo específico para o objeto domínio;
  • Validar se as configurações do cliente http estão corretas (ex: limite de tempo, tentativas, cabeçalho, etc);
  • Validar se o gateway se comporta como o esperado em caso de erros na rede.

Para testar todos estes pontos devemos executar o serviço que o gateway se comunica e executar um teste no serviço real. Isso parece ser uma boa solução mas existem alguns problemas ao fazer isso:

  • Precisamos saber como iniciar o serviço do provedor a partir do serviço do consumidor e também todos os serviços transitivos dos quais o provedor depende, bem como os bancos de dados necessários. Isso parece uma violação da responsabilidade única, o serviço do consumidor deve saber apenas como implantar a si mesmo e não os demais serviços dependentes;

  • No caso em que qualquer serviço precise de um banco de dados, um conjunto de dados deve ser preparado;
  • Iniciar diversos serviços implica que qualquer um deles pode falhar devido a um erro interno ou na rede, e consequentemente fazer com que o teste falhe, não por causa de um erro na classe do gateway, mas sim por um erro de infraestrutura, fazendo desse teste um teste falho;
  • Além disso, iniciando todos os serviços requeridos (mesmo que seja apenas um) pode consumir muito tempo, logo o conjunto de teste não fornece um feedback imediato.

Uma das soluções seria pular este tipo de testes por serem falhos e levarem muito tempo para serem executados, pelo simples fato de precisarem executar todo o universo para conseguir fazer com que um simples teste de gateway aconteça. Porém vale lembrar que a comunicação em uma arquitetura de microservices é a peça central, é exatamente nessa parte que qualquer interação com o sistema ocorre, por isso é tão importante ser testada e validada para que se comporte como o esperado.

A solução para este problema é a virtualização de serviços.

O que é virtualização de serviços?

A virtualização de serviços é uma técnica utilizada para simular os comportamentos das dependências de um determinado serviço. Apesar do fato da virtualização de serviços geralmente estar associada com serviços baseados em APIs REST, o mesmo conceito pode ser aplicado a qualquer outro tipo de dependência, como banco de dados, ESBs, JMS, etc.

Além de ajudar a testar os serviços internos, a virtualização de serviços também ajuda a testar serviços que não estão sob o nosso controle, evitando alguns problemas comuns que fazem esse tipo de teste serem fracos. Alguns deles são:

  • A rede está offline, nesse caso não é possível nos comunicar com um serviço externo;
  • O serviço externo está offline, e recebemos alguns erros inesperados;
  • Limite na API. Algumas APIs públicas têm limitações de requisições por um determinado período de tempo. Se alcançarmos esse limite os testes começarão a falhar.

Com a virtualização de serviços podemos evitar todos esses problemas desde que não estejamos requisitando o serviço real, mas sim o serviço virtual.

A virtualização de serviços pode ser utilizada além de apenas testar os melhores casos, como muitos desenvolvedores e testadores acham que o real poder estão nos testes quando estão próximo do limite do serviço pois é de extrema dificuldade testar os serviços reais como se comportam em casos de baixa latência ou de erros inesperados.

Se pensarmos sobre como estamos testando os componentes em uma arquitetura monolítica, devemos usar algo similar, porém entre objetos, e isso é chamado de mockar. Quando usamos o mock, estamos simulando o comportamento de um objeto providenciando uma resposta direta a chamada de um método. Quando usamos virtualização de serviços, estamos fazendo algo parecido, mas ao invés de simular o comportamento de um objeto, estamos providenciando uma resposta direta de um serviço remoto. Por esse motivo a virtualização de serviços algumas vezes pode ser conhecida como "mocking for enterprises".

Na próxima imagem veremos como a virtualização de serviços funciona:

Nesse caso a comunicação com o serviço está acontecendo através do protocolo HTTP, para isso um pequeno servidor é usado, e é o responsável por consumir as requisições da classe gateway assim como fornecer as respostas.

Modos de execução

De maneira geral, existem dois modos para virtualização de serviços:

  • Modo reprodução: Usa simulação de dados para providenciar uma resposta ao invés de encaminhar para um serviço real. A simulação de dados pode ser criada manualmente (quando o serviço real ainda não existe) ou usando o modo de captura;
  • Modo de gravação: intercepta a comunicação entre dois serviços gravando as requisições e as respectivas respostas que retornam do serviço real. Normalmente o modo de captura é usado como o ponto de início no processo de criação da simulação de dados.

Dependendo da implementação pode ser que existam outros módulos, mas todos esses devem conter esses dois (reprodução ou gravação).

Hoverfly

O que é Hoverfly?

O Hoverfly é uma ferramenta de simulação com uma API de virtualização de serviços escrita em Go, leve e open-source. Além disso o Hoverfly é fortemente integrado ao Java.

Java Hoverfly

O Java Hoverfly é um pacote de abstração do Hoverfly para a linguagem Java, nos deixando livre da instalação e do gerenciamento do ciclo de vida do Hoverfly. O Java Hoverfly oferece uma DSL em Java para simulação de dados via código e é fortemente integrado com JUnit e Junit5.

O Hoverfly Java configura as propriedades de rede do Java para utilizar o proxy do Hoverfly. Isso faz com que toda a comunicação entre a execução do Java e a camada de rede seja interceptada pelo proxy do Hoverfly. Isso significa que embora uma conexão HTTP com um cliente Java possa estar apontando para um site externo como http://worldclockapi.com, a conexão será interceptada e encaminhada pelo Hoverfly.

Vale lembrar que precisaremos configurar manualmente as configurações de rede no caso do nosso cliente http não honrar as configurações de proxy do Java.

Deste ponto em diante, vamos utilizar o Hoverfly e o Hoverfly Java de forma intercambiável para nos referenciarmos especificamente ao Hoverfly Java.

Para que o Hoverfly utilize o Junit 5 precisamos registrar a seguinte dependência no pom:

 <dependency>
       <groupId>io.specto</groupId>
       <artifactId>hoverfly-java-junit5</artifactId>
       <version>0.11.5</version>
       <scope>test</scope>
   </dependency>

Modos do Hoverfly

Além do modo Playback (reprodução) (no Hoverfly é chamado de Simulate) e gravação (dentro do Hoverfly chamamos de Capture), o Hoverfly também oferece outros métodos:

  • Spy: Simula APIs externas, se uma requisição é encontrada na simulação de dados será retornada caso contrário a requisição é direcionada para a API real;
  • Synthesize: Ao invés de buscar respostas na simulação de dados, a requisição é enviada diretamente para o middleware, que será responsável por consumir e gerar a resposta solicitada;
  • Modify: As requisições são enviadas para o middleware antes de serem enviados para o destino. As respostas também passarão pelo middleware antes de serem recebidas pelo cliente;
  • Diff: Encaminha a requisição para o serviço externo e compara a resposta com os dados salvos na simulação. Com ambas respostas o Hoverfly é capaz de detectar diferenças que são armazenadas para serem utilizadas no futuro.

Exemplo no Hoverfly

Para demonstrar como o Hoverfly trabalha, vamos supor que temos um serviço A (serviço de segurança) que precisa saber qual o tempo atual de um outro serviço que está sendo executado em http://worldclockapi.com. Quando uma requisição GET é solicitada para http://worldclockapi.com/api/json/cet/now o JSON abaixo é retornado:

{
   "$id":"1",
   "currentDateTime":"2019-03-12T08:10+01:00",
   "utcOffset":"01:00:00",
   "isDayLightSavingsTime":false,
   "dayOfTheWeek":"Tuesday",
   "timeZoneName":"Central Europe Standard Time",
   "currentFileTime":131968518527863732,
   "ordinalDate":"2019-71",
   "serviceResponse":null
}

O campo importante no JSON é o campo currentFileTime que contém a informação de data completa.

A classe do gateway que é responsável pela comunicação e responsável por retornar a data se parece com o código abaixo:

public class ExternalWorldClockServiceGateway implements WorldClockServiceGateway {
   private OkHttpClient client;

   public ExternalWorldClockServiceGateway() {
       this.client = new OkHttpClient.Builder()
           .connectTimeout(20, TimeUnit.SECONDS)
           .writeTimeout(20, TimeUnit.SECONDS)
           .readTimeout(30, TimeUnit.SECONDS)
           .build();
   }

   @Override
   public LocalTime getTime(String timezone) {
       final Request request = new Request.Builder()
           .url("http://worldclockapi.com/api/json/"+ timezone + "/now")
           .build();

       try (Response response = client.newCall(request).execute()) {
          
           final String content = response.body().string();
           final JsonObject worldTimeObject = Json.parse(content).asObject();
           final String currentTime = worldTimeObject.get("currentDateTime").asString();
           final DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
           LocalDateTime localDateTime = LocalDateTime.parse(currentTime, formatter);
           return localDateTime.toLocalTime();

       } catch(IOException e) {
           throw new IllegalStateException(e);
       }
   }

}

Vale lembrar que uma parte importante sobre o código é que a URL não é um parâmetro. Sabemos que em um exemplo real tal informação deveria ser recebida de um parâmetro de configuração, mas para manter a simplicidade no código e perceber como o Hoverfly gerencia toda as comunicações de rede a URL está fixa no código.

Vamos ver alguns cenários e como testar a classe ExternalWorldClockGateway.

O serviço World Clock ainda não foi disponibilizado.

Caso a classe WorldClockService não tenha sido desenvolvida, precisamos utilizar o Hoverlfy em modo de simulação e providenciar a devida resposta.

@ExtendWith(HoverflyExtension.class)
public class ExternalWorldClockServiceGatewayTest {

   private static final String OUTPUT = "{\n"
       + "   \"$id\":\"1\",\n"
       + "   \"currentDateTime\":\"2019-03-12T10:54+01:00\",\n"
       + "   \"utcOffset\":\"01:00:00\",\n"
       + "   \"isDayLightSavingsTime\":false,\n"
       + "   \"dayOfTheWeek\":\"Tuesday\",\n"
       + "   \"timeZoneName\":\"Central Europe Standard Time\",\n"
       + "   \"currentFileTime\":131968616698822965,\n"
       + "   \"ordinalDate\":\"2019-71\",\n"
       + "   \"serviceResponse\":null\n"
       + "}";

   @Test
   public void should_get_time_from_external_service(Hoverfly hoverfly) {
      
       // Given

       hoverfly.simulate(
           SimulationSource.dsl(
               HoverflyDsl.service("http://worldclockapi.com")
                   .get("/api/json/cet/now")
                   .willReturn(success(OUTPUT, "application/json"))
           )
       );

       final WorldClockServiceGateway worldClockServiceGateway = new ExternalWorldClockServiceGateway();

       // When
       LocalTime time = worldClockServiceGateway.getTime("cet");

       // Then
       Assertions.assertThat(time.getHour()).isEqualTo(10);
       Assertions.assertThat(time.getMinute()).isEqualTo(54);

   }

}

A parte importante nesse código é o método simulate. Esse método é utilizado para importar as iterações entre os testes e a simulação para o proxy remoto do Hoverfly. Como neste exemplo real, o proxy do Hoverfly é configurado para retornar uma resposta fixa ao invés de encaminhar o tráfego para fora do host local quando uma requisição GET é recebida naquele endpoint.

Word Clock Service foi desenvolvido e está funcionando.

Em vez de gerarmos os dados da simulação manualmente, podemos utilizar o modo de captura para gerar os dados iniciais da simulação, nesse caso o serviço já deve estar em execução.

@ExtendWith(HoverflyExtension.class)
@HoverflyCapture(path = "target/hoverfly", filename = "simulation.json")
public class ExternalWorldClockServiceGatewayTest {

   @Test
   public void should_get_time_from_external_service() {
      
       // Given

       final WorldClockServiceGateway worldClockServiceGateway = new ExternalWorldClockServiceGateway();

       // When
       LocalTime time = worldClockServiceGateway.getTime("cet");

       // Then
       Assertions.assertThat(time).isNotNull();

Nesse teste o Hoverfly foi iniciado em modo de captura. Isso significa que a requisição acontece no serviço real, e a requisição e a resposta são armazenadas localmente para serem reutilizadas no modo de simulação. No teste anterior os dados da simulação foram armazenados na pasta /target/hoverfly.

Uma vez que os dados para a simulação foram armazenados, podemos alterar o Hoverfly para o modo simulação, e assim nenhuma requisição futura será feita solicitando o serviço real.

@ExtendWith(HoverflyExtension.class)
@HoverflySimulate(source =
   @HoverflySimulate.Source(value = "target/hoverfly/simulation.json",
                            type = HoverflySimulate.SourceType.FILE))
public class ExternalWorldClockServiceGatewayTest {

   @Test
   public void should_get_time_from_external_service() {
      
       // Given

       final WorldClockServiceGateway worldClockServiceGateway = new ExternalWorldClockServiceGateway();

       // When
       LocalTime time = worldClockServiceGateway.getTime("cet");

       // Then
       Assertions.assertThat(time).isNotNull();

A anotação HoverflySimulate permite importar dados para a simulação de arquivos, classpath ou de uma URL.

Podemos utilizar o HoverflyExtension para alternar entre os modos simulação e captura automaticamente. Se os dados não forem encontrados, será executado em modo de captura, caso contrário o modo simulação é o que será executado. Isso significa que não precisamos ficar alternando manualmente entre @HoverflyCapture e @HoverflySimulate. Esta funcionalidade torna tudo muito mais fácil de ser utilizado além de ser muito poderosa.

@HoverflySimulate(source =
   @HoverflySimulate.Source(value = "target/hoverfly/simulation.json",
                            type = HoverflySimulate.SourceType.FILE),
                            enableAutoCapture=true)

Detectando dados desatualizados no modo simulação

Uma preocupação que devemos ter em mente quando utilizamos virtualização de serviços é o que irá acontecer no caso de nossos dados da simulação se tornarem obsoletos, nesse caso todos os testes vão passar, e podemos receber alguns erros quando apontamos o nosso código para o serviço real.

Para detectar esse tipo de problema o Hoverfly implementa o modo Diff que basicamente envia uma requisição para o serviço remoto e compara com os dados de simulação armazenados. Quando o sistema finalizar essa tarefa de comparação, armazenará os dados e responderá a requisição com os dados reais do serviço remoto.

@HoverflyDiff(
   source = @HoverflySimulate.Source(value = "target/hoverfly/simulation.json",
       type = HoverflySimulate.SourceType.CLASSPATH))

Normalmente não queremos executar o modo Diff o tempo todo. Na prática isso depende de vários fatores como, se controlamos o serviço remoto ou não, se foi fortemente desenvolvido ou não, etc. Dependendo das circunstâncias podemos querer executar os testes no modo comparação uma vez por dia, uma vez por semana ou sempre que planejarmos liberar uma nova funcionalidade.

Um fluxo de trabalho para verificar essa tarefa é definido abaixo:

  1. Testes são executados no Diff mode;
  2. Se ocorrer alguma falha então remova os dados obsoletos;
  3. Chamar o serviço de build para forçar o Hoverfly a recapturar os dados da simulação;
  4. O build pode estar falhando devido aos novos dados capturados serem diferentes dos dados previamente capturados. Nessa casos os desenvolvedores verão que o serviço está falhando e então podem começar a corrigir o código para aceitar os novos tipos de dados.

Atrasando respostas

O Hoverfly permite que adicionemos algum atraso nas respostas antes de enviá-las. Isso permite a simulação de latência e também permite testar se o serviço pode lidar com esse tipo de comportamento corretamente. Para configurar esta funcionalidade basta utilizar Simulation DSL para configurar o tempo de atraso que será aplicado.

hoverfly.simulate(
           SimulationSource.dsl(
               HoverflyDsl.service("http://worldclockapi.com")
                   .get("/api/json/cet/now")
                   .willReturn(success(OUTPUT, "application/json")
                       .withDelay(1, TimeUnit.MINUTES)
           )
       ));

Verificação

Conforme mencionado devemos estar pensando que virtualização de serviços é apenas mocking para "empresas". Uma das funcionalidades mais utilizadas no mocking é que podemos verificar qual método foi chamado durante uma fase de teste.

Com o Hoverfly podemos fazer exatamente o mesmo, verificando que requisições específicas foram realizadas para quais serviços remotos.

hoverfly.verify(

           HoverflyDsl.service("http://worldclockapi.com")

               .get("/api/json/cet/now"), HoverflyVerifications.times(1));

O trecho do código anterior verifica que a classe do gateway conseguiu se comunicar com o endpoint /api/json/cet/now do host worldclockapi.com.

Outras funcionalidades

O Hoverfly implementa algumas funcionalidades que não foram mostradas nesse artigo mas que são úteis em alguns casos.

  • Validação de campos da requisição: Por padrão, o construtor de requisição DSL assume uma correspondência exata quando utilizamos uma String. Também podemos passar um validador, nesse caso o DSL não fica restrito (ex: service(matches("www.*-test.com")));
  • Templates de resposta: Quando é necessário criar uma resposta dinâmica com base nos dados da requisição, podemos fazer isso utilizando templates;
  • SSL: Quando uma requisição passa pelo Hoverfly, precisa ser descriptografada e então ser armazenada (em modo de captura) ou ser validada. Nesse caso acabamos tendo o SSL entre o Hoverfly e o serviço remoto, e o SSL entre o cliente e o Hoverfly. Para deixar esse processo simples, o Hoverfly tem o próprio certificado que é criado automaticamente quando o iniciamos.
  • Simulação de estado completo: Algumas vezes precisaremos adicionar algum estado em uma resposta dependendo da requisição anterior. Por exemplo, após enviar uma requisição de delete, gostaríamos de receber um status de erro 404 no caso de a requisição requisitar o elemento deletado.
SimulationSource.dsl(
           service("www.example-booking-service.com")
               .get("/api/bookings/1")
               .willReturn(success("{\"bookingId\":\"1\"}", "application/json"))

               .delete("/api/bookings/1")
               .willReturn(success().andSetState("Booking", "Deleted"))

               .get("/api/bookings/1")
               .withState("Booking", "Deleted")
               .willReturn(notFound())

No código anterior, quando uma solicitação é enviada para /api/bookings/1 usando o método HTTP DELETE, o Hoverfly define o estado como excluido. Quando uma solicitação é enviada para /api/bookings/1 usando o método GET HTTP, um erro não encontrado é retornado, pois o estado é de excluído e não de pesquisa.

Testes de contrato

A virtualização de serviços é uma outra ferramenta que pode lhe ajudar a escrever testes, mas não é um substituto para os testes de contrato..

O foco principal dos testes de contrato é para validar se ambos, consumidor e o provedor de serviços estão aptos a se comunicarem corretamente do ponto de vista de negócios, ou seja, se ambos estão seguindo os contratos que concordaram em seguir.

Por outro lado, a virtualização de serviços pode ser utilizada para:

  • Testar serviços que já foram criados, porém ainda não seguem nenhum contrato;
  • Testar integrações com serviços de terceiros no qual podemos não ter o contrato;
  • Testar casos extremos (atrasos, entradas inválidas, …);
  • Auxiliar na criação de testes de integração no qual o cliente realiza alterações com maior frequência que o serviço dependente;
  • Testar o serviço dependente quando está indisponível ou se o custo é muito alto para execução de testes de carga;
  • Rotatividade de serviços para minimizar os rastros de memória;
  • Testes que não são difíceis de serem criados ou mantidos como acontece no caso de testes de contrato.

Conclusão

Virtualização de serviços (e o Hoverfly) é uma outra ferramenta que podemos utilizar para testar os nossos serviços, especificamente para testar a comunicação entre os serviços, podemos fazer isso utilizando uma abordagem baseada em testes de "unidade". Quando falamos "unidade" é pelo motivo de que o teste executado não está executando o serviço remoto, então estamos apenas testando uma unidade do sistema. Isso nos permite testar a camada de comunicação sem precisar executar o sistema inteiro e podemos fazer isso sem precisar gastar todo o tempo necessário que seria gasto para iniciar o sistema inteiro. Note que do ponto de vista do serviço consumidor que está sendo testado, não sabe nada sobre o serviço provedor, sendo assim um serviço virtual não é diferente de um serviço real. Isso significa que o serviço consumidor não faz a menor ideia se está vivendo em uma simulação ou não.

Com o advento da arquitetura de microservices, a virtualização de serviços é uma ferramenta essencial para evitar surpresas na comunicação com outros serviços, especialmente quando trabalhando no contexto empresarial com uma grande quantidade de dependências. A virtualização de serviços pode ser usada para remover a dependência de serviços de terceiros durante a fase de testes, e para testar como a aplicação se comporta quando encontra cenários de latência ou qualquer outro tipo de problema na rede. Finalmente, a virtualização de serviços pode ser utilizada com projetos legados, no caso de existir um monolito que exige o acesso a serviços de terceiros que são desafiados a outra forma de simulação.

Sobre o autor

Alex Soto é Engenheiro de Software do Red Hat apaixonado pelo mundo Java e por automação de software e acredita no modelo de software livre. Alex é o criador dos projetos NoSQLUnit e Diferencia, membro da JSR374 (API Java e Processamento de JSON) grupo de especialistas, é co-autor do livro Testing Java Microservices da Manning e contribuidor de diversos projetos open source. Java Champion desde 2017 e um palestrante internacional, palestrando sobre assuntos como novas técnicas de testes para microservices e entrega contínua no século 21. Você pode segui-lo no Twitter @alexsotob.

Avalie esse artigo

Relevância
Estilo/Redação

Olá visitante

Você precisa cadastrar-se no InfoQ Brasil ou para enviar comentários. Há muitas vantagens em se cadastrar.

Obtenha o máximo da experiência do InfoQ Brasil.

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Comentários da comunidade

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

BT

Seu cadastro no InfoQ está atualizado? Poderia rever suas informações?

Nota: se você alterar seu email, receberá uma mensagem de confirmação

Nome da empresa:
Cargo/papel na empresa:
Tamanho da empresa:
País:
Estado:
Você vai receber um email para validação do novo endereço. Esta janela pop-up fechará em instantes.