Este artigo merece sua atenção se você São apaixonados por escrever software de boa qualidade e desejam melhorar a estabilidade do seu aplicativo com testes. Estão cansados de ver bugs inesperados surgindo em seus sistemas de produção. Precisa de ajuda para entender o que são testes automatizados e como abordá-los. Por que precisamos de testes automatizados? Como engenheiros, queremos , mas com cada novo recurso que criamos, inevitavelmente aumentamos o tamanho e a complexidade dos nossos aplicativos. construir coisas que funcionem À medida que o produto cresce, torna-se cada vez mais demorado testar (por exemplo, com as mãos) todas as funcionalidades afetadas pelas alterações. manualmente A ausência de testes automatizados nos leva a gastar muito tempo e diminuir a velocidade de envio ou a gastar muito pouco para economizar velocidade, resultando em novos bugs no backlog junto com as ligações noturnas do PagerDuty. Pelo contrário, . Então, os computadores podem ser programados para fazer o mesmo repetidamente vamos delegar os testes aos computadores! Tipos de testes A sugere . Vamos nos aprofundar em cada tipo e entender por que precisamos de cada um. ideia da pirâmide de testes três tipos principais de testes: unidade, integração e ponta a ponta Testes unitários Uma é um pequeno pedaço de lógica que você testa (sem depender de outros componentes). unidade isoladamente Eles terminam em segundos. permite que eles sejam executados a qualquer momento, localmente e no CI, sem ativar os serviços dependentes/fazer chamadas de API e de banco de dados. Os testes unitários são rápidos. O isolamento uma função que aceita dois números e os soma. Queremos chamá-lo com argumentos diferentes e afirmar que o valor retornado está correto. Exemplo de teste de unidade: // Function "sum" is the unit const sum = (x, y) => x + y test('sums numbers', () => { // Call the function, record the result const result = sum(1, 2); // Assert the result expect(result).toBe(3) }) test('sums numbers', () => { // Call the function, record the result const result = sum(5, 10); // Assert the result expect(result).toBe(15) }) Um exemplo mais interessante é o componente React que renderiza algum texto após a conclusão da solicitação da API. Precisamos simular o módulo API para retornar os valores necessários para nossos testes, renderizar o componente e afirmar que o HTML renderizado tem o conteúdo que precisamos. // "MyComponent" is the unit const MyComponent = () => { const { isLoading } = apiModule.useSomeApiCall(); return isLoading ? <div>Loading...</div> : <div>Hello world</div> } test('renders loading spinner when loading', () => { // Mocking the API module, so that it returns the value we need jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Loading...').toBeInTheDocument() }) test('renders text content when not loading', () => { // Mocking the API module jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Hello world').toBeInTheDocument() }) Testes de Integração Quando sua interage com outras , chamamos isso de . Esses testes são mais lentos que os testes de unidade, mas testam como as partes do seu aplicativo se conectam. unidade unidades (dependências) integração um serviço que cria usuários em um banco de dados. Isso requer que uma instância de banco de dados ( ) esteja disponível quando os testes são executados. Testaremos se o serviço pode criar e recuperar um usuário do banco de dados. Exemplo de teste de integração: dependência import db from 'db' // We will be testing "createUser" and "getUser" const createUser = name => db.createUser(name) // creates a user const getUser = name => db.getUserOrNull(name) // retrieves a user or null test("creates and retrieves users", () => { // Try to get a user that doesn't exist, assert Null is returned const nonExistingUser = getUser("i don't exist") expect(nonExistingUser).toBe(null); // Create a user const userName = "test-user" createUser(userName); // Get the user that was just created, assert it's not Null const user = getUser(userName); expect(user).to.not.be(null) }) Testes ponta a ponta É um teste quando testamos o , onde todas as suas dependências estão disponíveis. Esses testes simulam melhor o comportamento real do usuário e permitem detectar em seu aplicativo, mas são o tipo de teste . ponta a ponta aplicativo totalmente implantado todos os problemas possíveis mais lento Sempre que quiser executar testes ponta a ponta, você deverá provisionar toda a infraestrutura e garantir que provedores terceirizados estejam disponíveis em seu ambiente. Você deseja tê-los para os recursos do seu aplicativo. apenas de missão crítica Fluxo de login. Queremos ir ao aplicativo, preencher os dados de login, enviá-lo e ver a mensagem de boas-vindas. Vamos dar uma olhada em um exemplo de teste completo: test('user can log in', () => { // Visit the login page page.goto('https://example.com/login'); // Fill in the login form page.fill('#username', 'john'); page.fill('#password', 'some-password'); // Click the login button page.click('#login-button'); // Assert the welcome message is visible page.assertTextVisible('Welcome, John!') }) Como você escolhe que tipo de teste escrever? Lembre-se de que . os testes ponta a ponta são mais lentos que os testes de integração e os testes de integração são mais lentos que os testes unitários Se o recurso no qual você está trabalhando for de missão crítica, considere escrever pelo menos um teste (como verificar como a funcionalidade Login funciona ao desenvolver o fluxo de autenticação). ponta a ponta Além dos fluxos de missão crítica, queremos testar o máximo possível de casos extremos e vários estados do recurso. nos permitem testar como as partes do aplicativo funcionam juntas. Os testes de integração Os endpoints devem executar as operações, produzir o resultado esperado e não gerar erros inesperados. Ter testes de integração para endpoints e componentes de clientes é uma boa ideia. Os componentes do cliente devem exibir o conteúdo correto e responder às interações do usuário de acordo com a forma como você espera que eles respondam. E finalmente, quando devemos escolher ? Todas as pequenas funções que podem ser testadas isoladamente, como que soma os números, que renderiza a tag , são ótimas candidatas para testes unitários. As unidades são perfeitas se você seguir a abordagem . testes unitários sum Button <button> de Desenvolvimento Orientado a Testes Qual é o próximo? (mas comece pequeno) Escreva alguns testes! adequada ao seu projeto/linguagem. Cada linguagem possui uma biblioteca popular para testes, como / para JavaScript, / para ponta a ponta (também usa JavaScript), para Java, etc. Instale uma estrutura de teste Jest Vitest Cypress Playwright JUnit Encontre uma pequena função em seu projeto e escreva um teste para ela. de unidade Escreva um teste para alguma interação componente/serviço-banco de dados. de integração Escolha um cenário crítico que possa ser testado rapidamente, como um fluxo de login simples, e escreva um teste para isso. completo Faça as coisas acima uma vez para entender como funciona. Em seguida, faça isso novamente durante algum trabalho de recurso/bug. Em seguida, compartilhe com seus colegas para que todos façam testes, economizem tempo e durmam melhor à noite! Recursos úteis: Meu blog pessoal de A Pirâmide do Teste Prático Ham Vocke por Desenvolvimento orientado a testes Martin Fowler