DEV Community

Alberto Luiz Souza
Alberto Luiz Souza

Posted on

Analisando Práticas de Teste do Projeto Hollow da Netflix: Lições de Código Open Source

Disclaimer
Este texto foi inicialmente concebido pela IA Generativa em função da transcrição de um vídeo do canal Dev + Eficiente. Se preferir acompanhar por vídeo, é só dar o play.

Introdução

Analisar código de projetos open source de empresas relevantes é uma excelente forma de evoluir suas práticas de desenvolvimento. Neste post, vamos explorar as práticas de testes automatizados do projeto Hollow da Netflix, focando em testes de unidade e integração. A ideia é comparar as práticas que você utiliza no seu contexto com as práticas de projetos mantidos por empresas de referência na indústria de software.

O Projeto Hollow

Hollow é um projeto open source da Netflix criado para facilitar a transferência de dados ultra rápida entre sistemas, especialmente quando lidamos com volumes gigantes de informação.

Práticas de Teste Observadas

Esforço Grande nos Asserts

Uma característica marcante nos testes do Hollow é o esforço investido na construção dos asserts. Por exemplo, no teste HollowRecordJsonStringifierTest, que testa o processo de stringificação de um record, há uma mensagem clara do que deve ser verificado, seguida do valor esperado e uma função auxiliar que facilita a construção do teste.

        String msg = "String types should be printed correctly";
        Assert.assertEquals(msg, "\"foo\"",
                stringifyType(TypeWithString.class, true, false, new TypeWithString("foo")));
        Assert.assertEquals(msg, "{" + NEWLINE
                        + INDENT + "\"value\": {" + NEWLINE
                        + INDENT + INDENT + "\"value\": \"foo\"" + NEWLINE
                        + INDENT + "}" + NEWLINE
                        + "}",
                stringifyType(TypeWithString.class, true, true, new TypeWithString("foo")));
Enter fullscreen mode Exit fullscreen mode

Não é fácil escrever asserts assim - pode ser até trabalhoso. Mas esse esforço compensa porque torna o teste mais claro e confiável. Hoje em dia é comum pedir para agentes de IA escreverem a primeira versão do teste, mas o importante é que o resultado final seja um teste robusto e compreensível.

Infraestrutura de Teste com Classes Abstratas

Outro padrão interessante é o uso de classes abstratas como infraestrutura para facilitar a escrita de testes. Por exemplo, a classe AbstractHollowRecordStringifierTest não segue a semântica tradicional de classes abstratas para polimorfismo, mas sim provê infraestrutura para você fazer seus testes.

public class AbstractHollowRecordStringifierTest {
    static class TypeWithString {
        private final String value;

        public TypeWithString(String value) {
            this.value = value;
        }
    }

    static class TypeWithPrimitive {
        private final int value;

        public TypeWithPrimitive(int value) {
            this.value = value;
        }
    }

    static class TypeWithNonPrimitive {
        private final Integer value;

        public TypeWithNonPrimitive(Integer value) {
            this.value = value;
        }
    }

    static class TypeWithNestedPrimitive {
        private final Double value;
        private final TypeWithPrimitive nestedType;

        public TypeWithNestedPrimitive(Double value, TypeWithPrimitive nestedType) {
            this.value = value;
            this.nestedType = nestedType;
        }
    }

...
Enter fullscreen mode Exit fullscreen mode

Quando seu projeto ganha escala e complexidade, você precisa investir tempo criando infraestrutura para facilitar a escrita dos testes. Se você tiver que lidar não apenas com a complexidade do código de produção, mas também com a complexidade acidental do código de teste, você aumenta a chance de diminuir a confiabilidade dos seus testes. Afinal, quanto mais esforço cognitivo você precisa para escrever um teste, maior a chance de deixar algo de fora.

Ausência Completa de Matchers

Em todos os testes analisados do projeto Hollow, há completa ausência de matchers. Não há verificações do tipo "qualquer string" ou matchers genéricos. Por quê?

Antes de qualquer coisa, seu teste deveria aumentar a confiabilidade do software. Se o teste não for confiável, que confiança para a equipe ele está gerando? Se você não confia de fato que aquele teste representa - quando ele passa - uma segurança de que aquilo tende a funcionar bem em produção, que confiança ele está passando? Na verdade é uma confiança falso-positiva: você acha que está confiante porque seu teste está verde, mas na verdade ele está te dizendo pouca coisa.

Muito cuidado com o uso de qualquer tipo de construção que não utilize os dados reais, os objetos reais, as construções reais que vão ser utilizadas em produção. Obviamente, de vez em quando você encontra barreiras de tempo de execução, infraestrutura complexa para subir, ou necessidade de integração com sistemas externos.

Testes Integrados e Setup Complexo

Os testes integrados do Hollow demonstram claramente que escrever testes confiáveis dá trabalho. Por exemplo, na classe HollowStateDeltaPatcherTest existem três iterações sobre o estado e, no final, faz várias asserções explicando cada uma com comentários. De vez em quando precisamos de comentários, não deveria ser um problema :).

Esse teste é integrado e não tem matcher nenhum, justamente para ficar mais próximo da realidade. Por quê? Porque você escreve o teste para aumentar a confiabilidade da execução do seu sistema.

Investimento em Infraestrutura

Um exemplo interessante é o HollowHistoryUITest, que mostra claramente o investimento em infraestrutura. Há quatro classes criadas especificamente para facilitar o teste. Existe um TestHollowConsumer.builder para criar objetos de teste, demonstrando bastante investimento de tempo para criar a infraestrutura necessária para executar esse teste super integrado.

        consumerExpected = new TestHollowConsumer.Builder()
                .withBlobRetriever(testBlobRetriever)
                .build();
        consumerExpected.triggerRefreshTo(1);
Enter fullscreen mode Exit fullscreen mode

Esse teste específico constrói muitos objetos em memória mas roda rápido - aproximadamente dois segundos após a inicialização, com tempo total de cerca de nove segundos.

Rápido ou Devagar: Uma Questão de Contexto

Nove segundos é rápido ou devagar? Depende de quantos testes você tem para executar e de como você executa o teste. Rápido ou devagar é uma avaliação, e avaliação tem contexto - medida é fria.

Como saber se é rápido ou devagar e como instruir isso com sua equipe? Sua equipe pode ter um direcional, como "você não deveria escrever nenhum teste que rode em mais de dez segundos". Se você tem milhares de testes, talvez isso passe a ser tenso e você precise ajustar. Faz parte do jogo.

Confiabilidade Antes de Facilidade

Se há uma lição principal a ser extraída da análise do projeto Hollow é esta: trazer confiabilidade é a prioridade.

Faça tudo que for preciso para manter altas as chances de escreverem testes que de fato tragam mais confiabilidade. Precisa investir em infraestrutura de testes? Invista.

O Cenário Estabelecido de Testes

O cenário de teste de software é um campo muito bem estabelecido. Não há grandes revoluções acontecendo constantemente(gerar teste com LLM não é revolução relacionada a qualidade de testes).

Nada do que foi mostrado aqui é algo revolucionário que você nunca viu antes. São práticas sólidas, aplicadas consistentemente, com foco no que realmente importa: confiabilidade.

Conclusão

Analisar código alheio, especialmente de produtos open source, é um exercício muito interessante. Te dá bagagem e capacidade de ler código legado, o que facilita muito quando você entra num ambiente mais corporativo de produção.

Use agentes de IA para auxiliar no processo de entendimento quando chegar em uma base de código nova. Eles podem ser a primeira camada de validação do seu entendimento.

E lembre-se: práticas de teste não precisam ser revolucionárias para serem eficazes. O projeto Hollow da Netflix demonstra que aplicar consistentemente os fundamentos - asserts claros, ausência de matchers, investimento em infraestrutura e foco na confiabilidade - é o caminho para testes de qualidade.

Dev+ Eficiente

Este conteúdo é parte do ecossistema Dev+ Eficiente, mantido por Alberto junto com Maurício Aniche e Rafael Ponte, que inclui um canal e dois treinamentos. O primeiro é a Jornada Dev+ Eficiente, cujo foco é fazer com que você seja capaz de entregar software que de fato gera valor com o máximo de qualidade e eficiência.

O segundo é a especialização em Engenharia de IA, uma parceria com Daniel Romero, cuja ideia é habilitar você para entregar software de excelência, integrando sistemas com LLMs.

Conheça mais em https://deveficiente.com/interesse-especializacao-engenharia-ia

Top comments (1)

Collapse
 
blenderman profile image
BBM

Vou começar a reforçar asserts detalhados e criar infraestrutura de teste, priorizando confiabilidade em vez de atalhos.