Suite de testes são conjuntos de testes para garantir que o código funcione corretamente. Testes unitários testam unidades individuais de código enquanto testes de feature testam funcionalidades completas. É importante desacoplar código de teste do código de produção e não testar trivialidades.
Desenvolvedor na Convenia
Trabalho com PHP a aproximadamente 7 anos
Um outro programa que testa o seu programa.
Não é uma prática nova, mas evoluiu muito com o tempo e aprendemos o que não fazer e o que não pode faltar.
Antigamente era possível encontrar equipes separadas escrevendo os códigos de produção e teste, isso se mostrou bem ineficiente e acabou morrendo com o tempo.
Jargão de testes amplo e sem consenso
Essa pirâmide pode variar um pouco dependendo do tipo de aplicação, os frameworks se desenvolveram muito em ferramentas de integração(laravel dusk) e percebo o mercado invertendo essa pirâmide aos poucos.
Apesar das vantagens absurdas, testar é muito caro, não envolve somente tempo, envolve formação
Encontra bastante lugar em times de produtos
Geralmente é deixado de lado em fábricas e consultorias e qualquer coisa que busque vender projetos como se fossem produtos(sinceramente não estão errados)
O custo não se justifica fora do contexto de sistema(site, institucional, blog....)
É uma honra trabalhar em um lugar que tem essa prática tão difundida
se o código está sob a minha governança, seria legal eu testar ele
O componente pode ser 3 coisas(um comando, uma consulta, uma função)
É legal tentarmos manter as coisas funcionais, porém aplicações web são focadas em I/O e a maior parte dos - Alguns componentes mesclam lógicas funcionais e códigos que causam side efects, seria uma boa ideia extrair o funcional.
A complexidade ciclomática está diretamente relacionada com o número de testes que você escreve para cobrir integralmente o código, a aplicação deve ter o número de testes aproximadamente igual ao numero de classes x CC média
Apesar da baixa complexidade ciclomática do método, o código contém muito comportamento implícito que deve ser testado.
seria interessante testar as exceptions que a classe dispara, o componente fala com o mundo externo através de exceptions, então devemos garantir a consistência dessa comunicação, é como se as exceptions que o código dispara fizesse parte da interface o objeto, elas inclusive podem ser declaradas com annotations.
se vc testar um endpoint Http, a forma como o endpoint vai falar com vc são status code, essa é justamente a função do exception handler, ele transforma uma exception em um status code, um erro de componente em um erro de endpoint, se vc realmente precisar verificar uma exception terá que desabilitar o exception handler.
Dublês é uma classe real que vc substitui por algum motivo, que veremos...
A lib mais difundida que temos para isso é o mockery
stubs x mocks x spies x fakes x dummies
O mockery, assim como a maioria das libs de teste não difere os tipos de dublês(com exceção do spie), na própria doc ele não faz questão nem de diferenciar um do outro, acredito que isso ocorra porque na prática existe pouca diferença entre um e outro em questão de setup.
classe final não pode ser mockada
A vibe do stub é prover o dado
O mock pode prover dados tbm(andReturn) mas sua finalidade principal é gravar as chamadas
Se vc passar algum parâmetro para a função seria legal fazer o assert desse parâmetro, fica mais seguro
Mockery::on é utilizado para assert em dados complexos
Particularmente não encontro muito uso para ele no meu dia a dia.
No mockery ele é similar ao mock, porém ele permite que você faça asserts de maneira mais convencional(arrange/act/assert).
Você não pode definir tipos de retorno e ele é mais permissivo em relação sequência das chamadas.
O mockery pode não ter a implementação mais conceitualmente correta de spie, na teoria o spie é um dublê que "envelopa" a classe original(a classe é utilizada de verdade) e assiste os métodos sendo chamados, com a possibilidade de testar algum estado da classe(propriedade).
Desacoplar métodos de uma mesma classe pode diminuir o número de testes necessários para cobrir aquela classe, e simplifica-los também
não use new
utilise Dependency injection
utilize app()
tente não utilizar estado em services
tente não utilizar services em jobs(Closures não podem ser serializáveis e mocks tem closures)
Possibilita sqlite in memory
possibilita coverage completo
facilita gerenciamento em geral
elimina fatores e latências externos
Para api e front separados com repo separados
Para api e front separados no mesmo repo(sendo servidos por uma rota)
Para api e front juntos(não SPA)
não rola coverage
no rola sqlite in memory
complicado por natureza(transições do front e sleeps são necessários)