https://github.com/isaacalves7/qa
🧪⚗️👨🏾🔬 Good practices of QA/QC (Quality Assurance and Quality Control) including TDD, BDD and DDD for Continuous testing from scratch.
https://github.com/isaacalves7/qa
automated-testing automation-testing bdd cicd clean-architecture clean-code continuous-integration continuous-testing cucumber ddd extreme-programming gherkin integration-testing observability qa quality-assurance refactoring solid tdd unit-testing
Last synced: about 1 month ago
JSON representation
🧪⚗️👨🏾🔬 Good practices of QA/QC (Quality Assurance and Quality Control) including TDD, BDD and DDD for Continuous testing from scratch.
- Host: GitHub
- URL: https://github.com/isaacalves7/qa
- Owner: IsaacAlves7
- Created: 2025-07-15T19:44:12.000Z (3 months ago)
- Default Branch: master
- Last Pushed: 2025-08-26T19:26:34.000Z (about 2 months ago)
- Last Synced: 2025-08-27T03:32:14.574Z (about 2 months ago)
- Topics: automated-testing, automation-testing, bdd, cicd, clean-architecture, clean-code, continuous-integration, continuous-testing, cucumber, ddd, extreme-programming, gherkin, integration-testing, observability, qa, quality-assurance, refactoring, solid, tdd, unit-testing
- Language: C#
- Homepage:
- Size: 12.3 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
[](https://opensource.org/licenses/Apache-2.0)
[](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
[](https://github.com/rife2/tests-badge/releases/latest)
[](https://github.com/rife2/tests-badge/actions/workflows/bld.yml)
[](https://github.com/rife2/tests-badge/actions/workflows/bld.yml)> Versículo chave: "Consagre ao Senhor tudo o que você faz, e os seus planos serão bem-sucedidos." - Provérbios 16:3
# QA/QC - Quality Assurance and Quality Control 👨🏾🔬
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
Imagine-se como um entusiasta da tecnologia, ansioso por entender como um software pode ser eficaz e confiável. Como garantir que um software atenda às expectativas dos usuários e funcione sem falhas? Esse dilema cria um conflito cognitivo que nos impulsiona a mergulhar mais fundo.
A história da **Qualidade de Software** nos mostra a evolução dos processos, desde as abordagens mais tradicionais até as metodologias ágeis modernas. Aprender sobre modelos de qualidade, como o ISO 25000, e as dimensões da qualidade, como funcionalidade e usabilidade, enriquece nossa compreensão. As peças se encaixam, transformando a questão inicial em um quebra-cabeça conceitual.
Imagine que você é um membro de uma equipe de desenvolvimento de software. Você é desafiado a aplicar os conceitos aprendidos em um cenário real. Você participa de revisões, que são análises colaborativas do código, e auditorias, que avaliam os processos utilizados. Essa simulação o coloca no ambiente profissional, onde as decisões têm impacto direto na qualidade do software.
Ao concluir as revisões e auditorias, você reflete sobre os processos e a eficácia das técnicas aplicadas. Reconhece a importância de identificar falhas cedo e como as revisões sistemáticas podem prevenir erros custosos. Você compreende que, assim como o software, o aprendizado é um processo contínuo (Continuous Learning). E à medida que você assimila esses conceitos, torna-se mais apto a contribuir para o desenvolvimento de software de alta qualidade, solidificando sua jornada na compreensão da Qualidade de Software
> [!Note]
> Esse conteúdo é geralmente abordado no final das graduações da área de TI, ou seja, é um módulo avançado e atua com boas práticas do mercado. Então eu vou supor que todos já sabem desenvolver softwares e pular toda a parte de codificação com metodologias ágeis e ciclo de vida de desenvolvimento para partir pro ponto de qualidade de software.> [!Tip]
> Vamos recordar a importância das boas práticas de programação como um elemento essencial na busca pela qualidade de software. Revisitarmos a noção de código limpo e bem estruturado, que não só facilita a manutenção, mas também reduz erros e falhas futuras. Recordar como padrões de nomenclatura consistentes, comentários claros e modularização do código são cruciais para criar um produto de software robusto e de fácil compreensão. Isso nos lembrará que a qualidade não se limita apenas aos processos de teste, mas começa desde a concepção do código-fonte.Primeiro, precisamos entender que a qualidade de software é uma área de grande importância no mercado de tecnologia da informação. E para garantir a qualidade de um software, é preciso conhecer seus fundamentos, entender sua história, compreender os custos envolvidos, realizar atividades de apoio, seguir padrões e avaliar seus atributos. A seguir, apresentamos alguns desses atributos:
> [!Important]
> **Definição**: Qualidade é a característica ou atributo que define algo ou alguém em termos de excelência, valor, ou nível de desempenho. Pode se referir a propriedades como durabilidade, eficiência, eficácia, ou valor percebido. “Qualidade é a medida de quanto um projeto atende aos requisitos especificados no escopo.”> [!Note]
> **FUNDAMENTOS DA QUALIDADE DE SOFTWARE**: Incluem a definição de requisitos, o planejamento de testes e a execução de testes de forma sistemática. Esses processos são essenciais para garantir que o software desenvolvido atenda às necessidades do usuário e atinja a qualidade esperada.No antigo Egito, há aproximadamente 4 mil anos, para que as construções fossem feitas com **qualidade**, definiu-se o cúbito, que era a distância do cotovelo à ponta do indicador do faraó. Uma das primeiras tentativas da humanidade de _padronizar_ as medidas, gerando qualidade nas construções.
Nesta escultura, temos na base um compilado com as regras e leis a serem seguidas, impostas por Hamurabi, Rei da Babilônia, que era aplicada a cidadãos livres, comerciantes, escravos, etc. Uma das primeiras tentativas de _padronizar_ as regras de convívio da sociedade, há aproximadamente 3.800 anos atrás.
Historicamente, o conceito de qualidade está ligado à ideia de atender padrões e requisitos acordados, com base em comunicação clara e objetivos definidos, seja na qualidade do ar, qualidade da água, da qualidade de vida ou qualidade do software. Portanto, a qualidade de software é a investigação do software a fim de fornecer informações sobre sua qualidade em relação ao contexto em que ele deve operar.
A busca por qualidade não é algo novo. Desde os primórdios, o ser humano busca aprimorar suas criações e produções, seja para torná-las mais duráveis, funcionais ou esteticamente agradáveis. Com o advento da Revolução Industrial, essa busca se intensificou, principalmente no que diz respeito à produção em massa.
Mas foi somente com a Segunda Guerra Mundial que a qualidade passou a ser vista como uma questão estratégica, com a necessidade de se produzir em larga escala produtos de alta qualidade para suprir as demandas militares. A partir daí, a qualidade começou a ser tratada como um processo e a ser estudada de forma mais sistemática. A evolução histórica da qualidade de software pode ser dividida em várias fases:
1. **PRIMEIRA FASE**: Conhecida como era dos “programadores heróis”, ocorreu na década de 1940 a 1950, quando o foco era na programação manual e não havia processos formais de garantia de qualidade.
2. **SEGUNDA FASE**: Foi a era dos “produtos de software”, que teve início nos anos 1960 e trouxe a necessidade de se preocupar com a qualidade dos softwares produzidos, mas ainda sem muita formalização.
3. **TERCEIRA FASE**: A era da “engenharia de software”, teve início nos anos 1970 e marcou o surgimento de processos formais de desenvolvimento de software e de metodologias para garantir a qualidade do produto final. Nesta fase, foi criada a ISO 9000, que estabeleceu padrões de qualidade para empresas e serviços em geral.
4. **QUALIDADE TOTAL**: Teve início na década de 1980, trouxe a ideia de que a qualidade não é responsabilidade apenas da área de desenvolvimento de software, mas sim de toda a organização. Foram estabelecidos processos de melhoria contínua e a necessidade de se envolver todos os departamentos e funcionários na busca pela qualidade.
5. **QUALIDADE DE SOFTWARE**: Teve início nos anos 1990, trouxe a necessidade de se preocupar com a qualidade do processo de desenvolvimento do software, e não apenas com o produto final. Surgiram novas metodologias e modelos de maturidade, como o Capability Matu
rity Model Integration (CMMI) e o ISO/IEC 12207, que buscam garantir a qualidade do processo de desenvolvimento.No início da década de 1980, o governo dos Estados Unidos reconheceu a necessidade de padronizar a qualidade do software. Em 1986, foi publicado o padrão IEEE 610.12, que definiu a terminologia básica usada em engenharia de software, incluindo definições de qualidade de software. Essas definições incluem “qualidade de software” como o grau em que um sistema, componente ou processo atende aos requisitos especificados e/ou implícitos e às necessidades ou expectativas do usuário.
A partir de então, a qualidade de software se tornou um foco importante na indústria de TI e foi acompanhada pelo surgimento de várias ferramentas e técnicas de teste e garantia de qualidade de software. A demanda por softwares confiáveis e de alta qualidade é cada vez maior, uma vez que muitos negócios e serviços dependem de sistemas de software em seus processos diários.
Hoje em dia, a busca pela qualidade de software continua em constante evolução, com a introdução de novas tecnologias e metodologias, como a integração contínua e o desenvolvimento ágil, e inteligência artificial que buscam garantir a qualidade do software de forma mais eficiente e rápida.
No entanto, a busca pela qualidade de software ainda é um desafio constante. Novas tecnologias e metodologias surgem a cada dia, e os desenvolvedores de software devem estar atualizados sobre as novas tendências e ferramentas disponíveis. Além disso, a complexidade dos sistemas de software modernos também torna o controle de qualidade mais difícil, exigindo uma abordagem mais estruturada e orientada a processos.
Portanto, os aspectos históricos da qualidade de software nos mostram como essa área evoluiu ao longo do tempo. Desde a década de 1970, a qualidade de software vem sendo debatida e estudada, e hoje em dia é um tema muito importante em empresas de desenvolvimento de software.
Para aprofundar seus conhecimentos sobre qualidade de software, você pode se perguntar: como garantir que um software seja de qualidade? Uma resposta é adotar processos de garantia de qualidade, que englobam atividades como revisões, testes e inspeções para identificar e corrigir defeitos. Além disso, é importante definir critérios de qualidade e medir a qualidade do software com base nesses critérios.
Uma ilustração que pode ajudar a entender a importância da qualidade de software é pensar em um aplicativo de banco que apresenta erros constantemente. Isso certamente afetaria a confiança do usuário no aplicativo e, consequentemente, na instituição financeira. Por isso, a qualidade do software é fundamental para garantir a satisfação e fidelização do usuário.Os fundamentos de **teste de software** (Software Testing) se referem a um conjunto de práticas e técnicas utilizadas para garantir que um software seja capaz de atender aos requisitos e expectativas dos usuários, além de estar livre de erros e falhas. Para ser eficaz, o teste de software deve ser planejado, executado e gerenciado de forma adequada. Isso envolve identificar e definir os objetivos do teste, estabelecer critérios de aceitação, selecionar as técnicas e ferramentas adequadas e documentar todo o processo.
Entre os fundamentos do teste de software estão:
1. **Identificação de requisitos**: Os requisitos são funcionalidades que o produto precisa ter para atender às expectativas e necessidades das partes interessadas. É importante que todos os requisitos do software estejam claramente definidos e documentados antes do início do teste. Essa etapa é crucial para garantir que o software atenda às expectativas dos usuários. O escopo é o conjunto de características desejadas que descrevem o resultado final do projeto.
2. **Planejamento de teste**: o planejamento do teste deve ser feito de forma estruturada e detalhada, incluindo a definição dos objetivos, recursos, cronograma, técnicas e critérios de aceitação.
3. **Projeto de casos de teste**: os casos de teste devem ser projetados de acordo com os requisitos e objetivos do teste, de forma a garantir a cobertura de todos os aspectos do software.
4. **Execução de testes**: a execução dos testes deve ser feita de forma sistemática e controlada, registrando todas as falhas encontradas.
5. **Avaliação de resultados**: os resultados dos testes devem ser avaliados de acordo com os critérios de aceitação para determinar se o software atendeu aos requisitos.
6. **Relatórios e documentação**: todos os resultados e conclusões do teste devem ser documentados de forma clara e concisa para permitir que outras pessoas entendam e repliquem o processo.
Além desses fundamentos básicos, existem diversas técnicas e ferramentas de teste que podem ser utilizadas para tornar o processo mais eficiente e eficaz. Essas incluem testes funcionais, testes de desempenho, testes de segurança, testes de usabilidade, dentre outros. É importante que o testador esteja familiarizado com as diferentes técnicas e saiba selecionar a mais adequada para cada situação.
Outro aspecto importante dos fundamentos de qualidade de software é a colaboração entre as equipes envolvidas no desenvolvimento. É preciso que todos os membros trabalhem em conjunto para garantir a qualidade do produto final. Isso inclui desde a comunicação eficiente entre os desenvolvedores e os testadores até a participação do usuário em testes de aceitação.
> [!Important]
> **Software Engineering: A Practitioner's Approach**: Para aprofundar seus conhecimentos sobre qualidade de software, você pode recorrer a materiais como artigos científicos, livros e cursos on-line. Uma indicação de leitura é o livro Software Engineering: A Practitioner's Approach, de Roger Pressman, que aborda temas como garantia de qualidade, medição de qualidade e processos de software. Em resumo, entender os fundamentos de qualidade de software é essencial para garantir o sucesso de projetos de desenvolvimento. Isso inclui a compreensão do conceito de qualidade, a adoção de processos de garantia de qualidade, a definição de critérios de qualidade e a colaboração entre as equipes. Aprofundar seus conhecimentos neste tema é fundamental para se destacar no mercado de trabalho e garantir a satisfação do usuário.Portanto, o teste de software (Software testing) para os profissionais de QA’s é a forma que utilizamos para avaliar um software, com um objetivo de assegurar e garantir a qualidade do mesmo, nos pontos de vista técnico e funcional.
O *teste de software* é a prática concreta que os profissionais de QA (no papel de testers, engenheiros de teste ou SDETs) usam para avaliar se o produto realmente cumpre o que deveria cumprir, tanto sob o ponto de vista técnico (estabilidade, performance, segurança) quanto funcional (se as regras de negócio e requisitos do usuário estão atendidos).
> "Descobrir o inesperado é mais importante do que confirmar o conhecido." - George E.P.Box
Quando falamos de testes de software usamos bastante os conceitos de dois pontos de vista: o _caminho testável_ e _caminho feliz_, mas cada um tem um propósito diferente dentro da estratégia de validação do sistema. O chamado “caminho feliz” (ou “happy path”) é aquele fluxo idealizado em que tudo acontece como esperado, sem erros, exceções ou desvios. É o cenário perfeito, onde o usuário ou o sistema segue exatamente o que foi planejado, com entradas válidas, comportamento conforme os requisitos e saída correta. É fundamental porque garante que a funcionalidade principal está realmente atendendo ao objetivo esperado quando usada da maneira correta, e por isso é quase sempre o primeiro caso de teste escrito, seja em testes manuais, automatizados, unitários ou de integração.
Já o “caminho testável” (alternativo ou inesperado) pode ter interpretações ligeiramente diferentes dependendo do contexto, mas normalmente é usado para se referir a qualquer rota dentro do código ou do fluxo da aplicação que pode ser exercitada por um teste. É o conjunto de caminhos possíveis que o teste consegue alcançar, incluindo o caminho feliz e também as rotas alternativas, os cenários de exceção, entradas inválidas, erros de negócio, falhas de integração, entre outros. Em outras palavras, não é só garantir que a função principal funciona, mas também que o sistema se comporta corretamente quando algo sai do esperado. É aí que entram testes negativos, de borda, de falha, de performance ou até mesmo de segurança, que exercitam a aplicação de forma mais ampla e realista.
Portanto, quando você ouve falar de “caminho feliz”, é um subconjunto do “caminho testável”. O caminho feliz prova que o básico funciona; o caminho testável inclui todas as outras possibilidades para dar robustez à aplicação. Ou seja, o usuário é inesperado, então vamos precisar "preparar" a aplicação para coisas inesperadas.
Dentro da distinção clássica entre *QA (Quality Assurance)* e **QC (Quality Control)**, os testes de software se enquadram como atividades de QC, porque estão no nível da inspeção e verificação do produto final. QA olha mais para os processos, prevenindo defeitos ao longo do ciclo (definição de padrões, auditorias de qualidade, práticas de engenharia), enquanto QC foca em detectar e validar defeitos no resultado tangível — o software em execução, as integrações funcionando, os requisitos sendo cumpridos.
Portanto, sim: quando falamos em **testes manuais, automatizados, de integração, de aceitação, de regressão, de performance, etc.**, estamos atuando dentro da **camada de QC**. Eles são o mecanismo pelo qual conseguimos transformar “expectativas de qualidade” em **evidências objetivas** de que o software funciona (ou não) como esperado.
> [!Important]
> Vantagens dos testes de software: Se você quer evoluir em testes de software e entender como integrar qualidade de forma colaborativa e contínua em equipes ágeis, o livro **"Agile Testing: A Practical Guide for Testers and Agile Teams"**, escrito por Lisa Crispin e Janet Gregory, é uma leitura essencial para transformar a teoria em prática.
>
> - Qualidade
> - Suporte ao time
> - Feedback constante
> - Produtividade
> - Documentação viva
> - Alinhamento com o negócio
> - Disseminação de conhecimento
> - Prevenção de bugs: Evitar a continuação dos bugs
> - Redução de CustosA definição de “pronto” nada mais é do que um contrato firmado entre o time e o PO, que lista de forma clara os requisitos que determinam que uma User Story está completa.
> E a resposta é simples: normalmente, quando perguntam se uma funcionalidade ou story está pronta, respondem: “sim, mas falta testar…”.
Isso significa que ao definir o conceito de “pronto”, é importante que o QA – que faz parte do time – esteja envolvido e possa sensibilizar os membros do time e PO para que os testes façam parte deste conceito.
Dessa forma, garantimos:
- Integração entre desenvolvedores e QA;
- Maior qualidade do time;
- Que não haja desentendimentos desnecessários;
- Story testada e com aceite formal.As atividades de apoio da qualidade de software, como revisões, auditorias e inspeções, são essenciais para garantir que o software seja produzido de acordo com os padrões de qualidade estabelecidos. Essas atividades podem ser realizadas em diferentes momentos do ciclo de vida do software, desde a análise de requisitos até a manutenção.
As **revisões** são uma das atividades de apoio mais comuns e envolvem a análise do software por um grupo de pessoas para detectar erros e possíveis me
lhorias. Existem diferentes tipos de revisões, como revisões de código (Code Reviews), revisões de documentos e revisões de design.
As **auditorias** são semelhantes às revisões, mas geralmente são realizadas por equipes independentes de pessoas com habilidades específicas para avaliar o software em relação a padrões e regulamentações específicos. As auditorias podem ser internas ou externas e podem ser realizadas em diferentes fases do ciclo de vida do software (BRITO, 2006).Já as **inspeções** são atividades mais formais e estruturadas, que seguem um processo específico para avaliar o software em relação a determinados padrões e especificações. As inspeções geralmente envolvem uma equipe multidisciplinar e podem ser realizadas em diferentes fases do ciclo de vida do software.
Essas atividades de apoio da qualidade de software são importantes porque ajudam a identificar problemas no software antes que ele seja lançado, reduzindo assim os custos e os riscos associados a falhas no software. Além disso, elas também podem ajudar a melhorar a eficiência e a eficácia do processo de desenvolvimento de software (SHAMA, 2016).
Para aprofundar seu conhecimento sobre atividades de apoio da qualidade de software, você pode se perguntar:
■ Quais são as diferenças entre as atividades de revisão, auditoria e inspeção?
■ Como as atividades de apoio podem ser integradas ao processo de desenvolvimento de software?
■ Qual é o papel dos diferentes membros da equipe na execução dessas atividades?
Além disso, uma ilustração útil pode ser um diagrama que mostre a relação entre as atividades de apoio da qualidade de software e outras atividades do ciclo de vida do software, como desenvolvimento, teste e implantação. Isso pode ajudar a entender como as atividades de apoio se encaixam no processo geral de desenvolvimento de software e como elas podem contribuir para a melhoria da qualidade do produto final.
É importante destacar que a observabilidade (observability) não é exatamente parte do QA no sentido tradicional, mas ela se conecta de forma muito natural com a qualidade do software. QA (Quality Assurance) historicamente é voltado para prevenção de defeitos, validação e verificação de requisitos, testes funcionais, não funcionais e processos que garantem que o produto atenda ao que foi especificado. Observabilidade, por outro lado, nasceu com foco na operação e na confiabilidade: é a capacidade de entender o que está acontecendo em um sistema, em tempo real, a partir de sinais como logs, métricas e traces. Ela vai além de simplesmente monitorar: é sobre ser capaz de responder perguntas desconhecidas sobre o comportamento do sistema.
A relação entre as duas áreas está no fato de que quanto mais observável um sistema é, mais fácil fica detectar, diagnosticar e corrigir problemas que impactam a qualidade. QA tradicional muitas vezes termina antes da aplicação ir para produção, mas bugs e falhas ainda podem aparecer em ambientes reais. Com observabilidade, você complementa os testes com uma visão contínua, onde é possível acompanhar a saúde, a performance, os gargalos e até o comportamento do usuário. Para equipes modernas, especialmente em DevOps e SRE, a linha entre QA e observabilidade se mistura um pouco, pois qualidade não é mais só “testar antes de subir”, mas também garantir que o produto em execução esteja saudável, rastreável e confiável.
Portanto, observabilidade pode ser vista como complementar ao QA, não substitui testes e processos de qualidade, mas amplia a segurança e o controle. Muitas equipes de alto desempenho hoje tratam a observabilidade como parte essencial da estratégia de qualidade, especialmente quando o sistema é distribuído, usa microsserviços, filas, integrações externas ou precisa de alta disponibilidade.
## [QA] Custos x Fases
Você sabia que a qualidade de um software pode impactar diretamente nos custos de uma empresa? Os **Custos de controle** em qualidade de software se referem aos gastos necessários para prevenir defeitos e garantir a qualidade dos produtos de software, bem como para corrigir quaisquer defeitos encontrados durante as fases de desenvolvimento
e teste.Esses custos podem incluir investimentos em treinamento, ferramentas de teste, testes manuais e automatizados, revisões de código e outras atividades de garantia de qualidade.
Ao investir em custos de controle, as organizações podem reduzir o número de defeitos encontrados nos produtos de software (custos de prevenção) e, consequentemente, minimizar os custos associados a falhas de software, como retrabalho, atrasos no cronograma, perda de receita e reputação negativa (custos de avaliação).
As falhas internas são aquelas que acontecem dentro da empresa, antes do produto final ser entregue ao cliente. Elas podem ser causadas por diversos motivos, como falta de capacitação da equipe, problemas de comunicação, dentre outros. Essas falhas podem levar a retrabalho, atrasos na entrega e insatisfação do cliente.Já as **falhas externas** são aquelas que ocorrem após a entrega do produto final ao cliente. Elas podem ser causadas por problemas na codificação, falhas de segurança, dentre outros fatores. Essas falhas podem gerar custos altíssimos para a empresa, como a necessidade de retrabalho, de suporte técnico, de processos judiciais e até mesmo a perda de clientes
> [!Warning]
> Para aprofundar seu conhecimento sobre os custos de qualidade de software, você pode se perguntar: quais são as principais causas de falhas internas e externas? Como a qualidade do software pode influenciar nos custos da empresa? Como é possível reduzir esses custos e melhorar a qualidade do produto final?
A **Regra 10 de Myers**, criada em 2004 por Glenford Myers, é uma importante diretriz para os analistas de teste de software. Essa regra afirma que toda falha no software deve ser rastreável a uma ação ou decisão específica tomada durante o desenvolvimento do software.
> A ilustração mostra um gráfico que representa os custos de correção associados a cada estágio do ciclo de desenvolvimento de software. No lado esquerdo, há uma linha vertical que corresponde ao eixo Y, onde está inscrito "Custo da correção do defeito". Na parte inferior, há uma linha horizontal que se junta à linha vertical, formando uma grade para marcar os estágios do ciclo no eixo X. Abaixo dessa linha vertical está escrito da esquerda para a direita: Análise, Especificação, Construção, Testes e Produção. No interior do gráfico há uma linha vermelha com cinco círculos amarelos.
Em outras palavras, a Regra 10 de Myers enfatiza que o custo da correção de defeitos é bem mais custoso quanto mais tarde o defeito é encontrado, ou seja, um defeito encontrado em produção custa muito mais do que se fosse encontrado na fase de análise ou em modelos de dados.
Uma boa gestão de qualidade pode ser a chave para minimizar os custos relacionados às falhas em software. Investir em capacitação da equipe, em boas práticas de desenvolvimento e em testes constantes pode ajudar a evitar falhas internas e externas e, consequentemente, reduzir os custos para a empresa.
O **Shift Left Testing** é um conceito dentro da engenharia de software e de testes que significa **trazer as atividades de teste para mais cedo no ciclo de desenvolvimento**. Tradicionalmente, em modelos como o *waterfall*, os testes só aconteciam no fim, depois que o código já estava implementado. Isso levava a atrasos, altos custos de correção e acúmulo de defeitos. O movimento de “shift left” (deslocar para a esquerda, no cronograma) é justamente antecipar a validação de qualidade para as fases iniciais — análise de requisitos, design e até antes de escrever código — com o objetivo de encontrar problemas cedo, quando ainda são baratos de corrigir.
E isso se conecta com a famosa Regra de 10 de Myers (ou Myers’ Rule of Ten). A regra, proposta por Glenford Myers, diz que:
> “O custo para corrigir um defeito aumenta em uma ordem de grandeza (10x) a cada fase posterior do ciclo de desenvolvimento em que ele é encontrado.”
Na prática, **shift left** se traduz em várias práticas modernas. Envolve escrever critérios de aceitação e testes antes da implementação (TDD, ATDD, BDD), fazer testes unitários e de integração contínuos, usar pipelines de CI/CD para rodar automaticamente baterias de testes a cada commit, e até aplicar ferramentas de análise estática e validações de segurança durante o desenvolvimento. Também pode incluir colaboração mais próxima entre desenvolvedores, QAs e analistas de negócio logo no início, evitando que requisitos mal entendidos só sejam percebidos no final.
O grande benefício é que, quanto mais cedo um bug é descoberto, menor o impacto técnico e financeiro para o projeto. Por isso, “shift left testing” não significa apenas testar antes, mas criar uma cultura de **qualidade incorporada desde o início**, em vez de tratada como etapa final. É um complemento direto de metodologias ágeis e DevOps, porque apoia ciclos rápidos, entregas frequentes e feedback constante.
> [!Important]
> **Qualidade De Software Na Prática - Como Reduzir O Custo De Manutenção**: Para aprofundar seus conhecimentos sobre avaliação de atributos de qualidade, recomendamos a leitura do livro Qualidade De Software Na Prática - Como Reduzir O Custo De Manutenção é um livro escrito por Alexandre Kherroubi e Adalberto Cavalcanti. O livro aborda a importância da qualidade de software na redução dos custos de manutenção e explora estratégias práticas para melhorar a qualidade do software, resultando em um impacto positivo nos custos de manutenção ao longo do ciclo de vida do software.## [QA] Padrões de Qualidade de Software
Os padrões de qualidade de software são conjuntos de práticas, regras e diretrizes que visam garantir a qualidade do software produzido. Eles são uma referência para desenvolvedores e equipes de qualidade, ajudando a garantir que o software produzido seja confiável, seguro e atenda aos requisitos do usuário.Os padrões de qualidade de software podem ser baseados em normas internacionais , como a ISO/IEC 12207 e a ISO/IEC 9126, ou podem ser específicos para uma indústria ou empresa. Eles podem cobrir todos os aspectos do ciclo de vida do software, desde a especificação de requisitos até a manutenção e evolução do software.
Os padrões de qualidade de software podem ser baseados em normas internacionais.
> VOCÊ SABE RESPONDER? Como os padrões de qualidade de software podem ajudar a melhorar a confiabilidade e segurança do software?
Um dos principais benefícios dos padrões de qualidade de software é que eles fornecem uma base sólida para a avaliação da qualidade do software (GALINAC, 2013). Os padrões de qualidade de software, como o **CMM (Capability Maturity Model)** e o **MPS-BR (Melhoria de Processo do Software Brasileiro)**, foram desenvolvidos para melhorar a qualidade dos processos e produtos de software.
O CMM foi criado pelo SEI (Software Engineering Institute) nos anos 1990, com o objetivo de estabelecer um modelo de maturidade para avaliar a capacidade dos processos de software em uma organização (CMMI-DEV V2.0, 2018).
O modelo é baseado em cinco níveis, que representam um aumento gradual da maturidade do processo, desde o nível 1, que é o mais baixo, até o nível 5, que é o mais alto. Cada nível tem seus próprios objetivos, práticas e áreas de processo.
O CMM é amplamente utilizado para avaliar a maturidade dos processos de software em organizações governamentais e privadas em todo o mundo.
## [QA] Self testing









O conceito de **self-testing** em software vem da ideia de que o próprio sistema deve ter meios embutidos para validar se está funcionando corretamente, sem depender de verificações externas e ocasionais. Em essência, é a noção de que a aplicação deve trazer consigo a capacidade de se “autoavaliar”.
Isso pode se manifestar de formas diferentes dependendo do contexto: em nível de código, por meio de uma suíte de testes automatizados que é executada sempre que o sistema é construído ou implantado; em nível operacional, com health checks, métricas, logs e alertas que confirmam se o serviço está íntegro; ou ainda em processos de QA, onde o produto é constantemente validado contra critérios de aceitação definidos.
Dentro desse conceito, **testes automatizados** são o núcleo natural do self-testing, porque permitem verificar de forma contínua e repetível se o código ainda atende às especificações à medida que evolui. É nesse ponto que práticas como **TDD** se encaixam perfeitamente: ao escrever primeiro o teste e depois o código, o desenvolvedor garante que cada parte do sistema já nasce coberta por verificações, criando um software que já vem com seus próprios “exames de saúde”. Além disso, TDD gera uma rede de segurança de regressão, que torna o sistema mais resistente a falhas ao longo do tempo.
> "Descobrir o inesperado é mais importante do que confirmar o conhecido." - George E.P.Box
Já os **testes manuais** também podem existir em um cenário de self-testing, mas não cumprem totalmente a proposta. Eles servem bem em situações exploratórias, de usabilidade ou de descoberta de casos não previstos, mas não garantem a automação e a repetibilidade que o conceito prega. Em outras palavras, um sistema pode até contar com testes manuais complementares, mas não se considera realmente “self-testing” se depende só deles, porque não há como o software se validar sozinho sem a intervenção humana.
Portanto, podemos dizer que o self-testing **aceita testes manuais como complemento**, mas a essência dele está nos **testes automatizados**, preferencialmente guiados por boas práticas como TDD ou ATDD, que dão disciplina e cobertura consistente ao ciclo de desenvolvimento.
Estudamos os branches, os workflows e concluímos que dentro da prática da integração contínua, devemos nos afastar o mínimo possível do nosso trunk master principal.
Com as alterações que realizamos o tempo todo em nosso software, como podemos garantir a qualidade do código? Testes. No caso da integração contínua, precisaremos utilizar testes automatizados. O ideal é que a cada alteração, seja realizado um novo teste automatizado, para termos certeza de nenhum problema será gerado.
- Testes fazem parte da construção do software;
- Devem ser realizados antes do commit;
- TDD pode ajudar neste processo;
- Desempenho bom em testes;
- Os testes demorados podem ser uma barreira para a integração contínua, por isso precisamos ficar atentos.## [QA] Automation testing










Conforme o tempo passa a tecnologia segue avançando e os sistemas que são desenvolvidos por pessoas da área de TI estão cada vez mais completos. Antigamente os testes manuais eram os mais utilizados, mas eles já não suprem mais às demandas das empresas e acabam sendo suscetíveis a erros. Então, as organizações precisam desenvolver mais e com melhor qualidade, é aí que entram os **testes automatizados** que são programas que executam testes em softwares que estão em construção de uma forma padronizada, sem ser necessário a intervenção humana.
Pois, tais testes possuem funcionalidades capazes de testar de forma automática todos os aspectos de uma plataforma, com o intuito de assegurar um desempenho adequado. Ou seja, a automação de teste é o uso de software para controlar a execução do teste de software, a comparação dos resultados esperados com os resultados reais, a configuração das pré-condições de teste e outras funções de controle e relatório de teste.
Tal procedimento, gera muito mais eficácia e agilidade na etapa de testes, permitindo que o profissional encontre de uma maneira mais fácil as falhas de segurança, bugs e demais erros que possam comprometer o uso da aplicação.
> Quando o profissional notar que está gastando muito tempo com tarefas repetitivas e quando o software está muito grande, pode ser a hora de automatizar. Mas, é necessário também questionar a viabilidade dessa ação, sendo essencial analisar se com a automação a equipe irá obter ganho de tempo e se conseguirão reduzir custos e manter a qualidade.
Testes automatizados são uma das práticas mais fundamentais no desenvolvimento de software moderno, pois garantem confiabilidade, reduzem bugs em produção, facilitam refatorações e melhoram a documentação viva do sistema. Para construir testes automatizados realmente bons, é preciso compreender não só as ferramentas, mas também o processo como um todo — desde a fase de planejamento até a execução contínua. Tudo começa pela compreensão dos **níveis de teste**:
![]()
1. **Testes de unidade** (isolam pequenas partes do código),
2. **Testes de integração** (verificam a comunicação entre partes),
3. **Testes de sistema** (validam o sistema como um todo) e
4. **Testes end-to-end** (simulam o comportamento real do usuário).
Cada nível exige atenção diferente e ferramentas específicas:
No início do ciclo, o **desenho dos testes** precisa ser baseado em critérios claros de cobertura: o que está sendo testado, por que está sendo testado e o que não precisa ser testado. Bons testes não são só aqueles que passam, mas aqueles que falham quando o comportamento do código foge do esperado. Para isso, as asserções precisam ser claras, específicas e rastreáveis. Boas práticas incluem escrever testes que sejam rápidos, isolados, determinísticos e legíveis. Um teste bom é aquele que alguém consegue entender o que ele verifica só de ler o seu nome e o corpo, sem necessidade de ir até a implementação testada.
A fase de ferramentas é tão importante quanto o planejamento. Para testes de unidade, temos ferramentas como **JUnit** (Java), **xUnit** (C#), **pytest** (Python), **Jest** e **Vitest** (JavaScript/TypeScript), **Elixir ExUnit**, entre outras. Para mocks e test doubles, usamos bibliotecas como **Moq**, **Sinon**, **Mockito** ou **NSubstitute**, que ajudam a isolar dependências externas, como chamadas a APIs, bancos de dados e arquivos. Em testes de integração, frameworks como **TestContainers**, **WireMock** ou bancos de dados em memória ajudam a montar ambientes realistas. Para testes de aceitação e end-to-end, ferramentas como **Cypress**, **Playwright**, **Selenium** e **Puppeteer** são as mais utilizadas, permitindo testes que interagem com o navegador ou sistema completo, validando fluxos reais.
![]()
Na construção de um bom teste automatizado, o primeiro passo é **nomear corretamente o que está sendo testado**, depois criar um ambiente previsível para que os testes não tenham falsos positivos ou negativos. Um teste que falha às vezes é um teste ruim. Depois, **seguir o padrão AAA** (Arrange, Act, Assert) é uma boa prática: configurar os dados e dependências, executar o comportamento que está sendo testado, e por fim verificar o resultado. Também é essencial não testar lógica interna demais (isso gera testes frágeis), mas focar no comportamento observável da função ou componente.
Outro pilar crucial é a **integração com pipelines de CI/CD**. Automatizar os testes via GitHub Actions, GitLab CI, Jenkins ou Azure DevOps garante que os testes rodam a cada push ou PR, evitando regressões. Um teste que só roda localmente é praticamente inútil em um time com múltiplos desenvolvedores.
Além disso, não se deve esquecer do **relato dos testes**. Ferramentas de coverage (cobertura de código) como **Istanbul**, **Coverlet** ou **Codecov** ajudam a visualizar o quanto do código está sendo testado, embora **cobertura alta não signifique qualidade alta** — é possível ter 100% de cobertura e testes inúteis. O ideal é buscar cobertura útil, ou seja, testes que validam fluxos importantes, limites, erros e casos reais de uso.
Por fim, construir um teste automatizado bom exige prática, disciplina e conhecimento. Não é só sobre ferramentas, mas sobre escrever código de teste que seja confiável, fácil de manter e que reflita as regras de negócio do sistema. É preciso ter clareza sobre o que vale a pena testar, manter a suíte de testes rápida e identificar o ponto de equilíbrio entre cobertura e custo de manutenção. Testes automatizados são investimento — e como todo investimento, precisam de foco, consistência e revisão contínua para darem retorno real.
O desenvolvimento, inspeção e o teste de unidade são as três partes do teste de códigos. Numa era onde tudo é automatizado, testadores de software tem demandado cada vez mais ferramentas de automação de testes. Veja algumas ferramentas para automação de testes:
![]()
1. **Selenium**: é um framework portátil para testar aplicativos web. O Selenium fornece uma ferramenta de reprodução para a criação de testes funcionais sem a necessidade de aprender uma linguagem de script de teste. É provavelmente a ferramenta de automação de testes mais conhecida e utilizada no mundo, especialmente quando o foco são aplicações web. Diferente de soluções comerciais como Ranorex, UFT ou TestComplete, ele é open source, o que significa que não há custo de licença e ele pode ser adaptado de acordo com as necessidades do time. Esse fator, aliado à sua flexibilidade, fez do Selenium uma espécie de padrão de fato em automação de testes web, sendo adotado desde startups até grandes corporações.
2. **Robot Framework**: é uma estrutura genérica de automação de teste para testes de aceitação e desenvolvimento orientado a testes de aceitação. É uma estrutura de teste orientada por palavras-chave que usa a sintaxe de dados de teste tabular.
3. **Robotium**: é um framework open source de automação de testes voltado especificamente para aplicações Android. Ele surgiu como uma resposta à necessidade de se criar testes funcionais e de interface de forma mais prática e menos trabalhosa do que o que era oferecido nativamente pela API de testes do Android. Diferente de soluções que focam em testes unitários ou apenas em pequenas partes da aplicação, o Robotium foi desenhado para permitir que você crie testes que simulam a interação real de um usuário com o aplicativo, clicando em botões, digitando em campos, navegando por telas e validando os resultados exibidos. Ele funciona essencialmente como uma camada que se apoia no JUnit e traz uma API mais rica e amigável para lidar com a interface de usuário.
4. **Cucumber**: é uma ferramenta de automação de testes que se popularizou por trazer uma abordagem muito forte de BDD (Behavior-Driven Development) para o dia a dia do desenvolvimento de software. Diferente de frameworks mais técnicos, que focam em testes unitários ou funcionais a partir do código, o Cucumber tem como diferencial a ideia de que os testes devem ser escritos em uma linguagem acessível a todos do time — não apenas a desenvolvedores. Ele utiliza a sintaxe Gherkin, que é baseada em descrições de comportamento no formato de cenários, usando estruturas como Dado–Quando–Então. Isso permite que pessoas de negócio, analistas de QA e desenvolvedores conversem sobre o sistema usando a mesma forma de especificação, diminuindo ambiguidades e garantindo que todos entendam da mesma maneira o que está sendo implementado.
5. **Playwright**: é uma ferramenta criada com foco em testes automatizados de aplicações web, mas seu propósito vai um pouco além disso, porque ele também pode ser usado como uma biblioteca de automação de navegação. A origem dele está na mesma equipe que desenvolveu o Puppeteer, do Google, mas a Microsoft o criou para oferecer mais robustez, abrangência e recursos modernos. O Playwright permite em testes com Cross-browser (Chrome Firefox etc) para aplicações web, emulação Mobile para simular dispositivos móveis, e testes de API trata requisições HTTP.
6. TestComplete: é uma ferramenta comercial de automação de testes desenvolvida pela SmartBear que tem como foco facilitar a criação, execução e manutenção de testes funcionais em diferentes tipos de aplicações, desde desktop, web até mobile. A grande proposta dela é oferecer uma plataforma robusta que permite tanto a quem tem experiência em programação quanto a quem não programa criar testes de forma eficiente. Isso é possível porque ela oferece duas formas de trabalho: por um lado, há a possibilidade de construir scripts completos em linguagens como Python, JavaScript, VBScript e outras suportadas, o que dá liberdade total para quem tem familiaridade com código; por outro lado, ela também disponibiliza recursos de gravação e reprodução, em que o testador interage com a aplicação e o TestComplete grava essas interações para gerar um script automaticamente, que depois pode ser reutilizado e refinado.
7. Telerik Test Studio: é uma ferramenta de automação de testes desenvolvida pela Progress, criada para simplificar e acelerar o processo de validação de software em diferentes tipos de aplicações, desde web, desktop até aplicativos responsivos e mobile. O grande diferencial dela é o foco em ser acessível para equipes que não necessariamente têm profundo conhecimento em programação, ao mesmo tempo em que entrega recursos avançados que podem ser explorados por engenheiros de testes mais experientes. Assim como outras soluções comerciais, a proposta é unir praticidade, estabilidade e integração com o ciclo de desenvolvimento.
8. HPE Unified Functional Testing: anteriormente conhecido como HP QuickTest Professional (QTP), é uma das ferramentas mais tradicionais e consolidadas de automação de testes funcionais. Ele foi desenvolvido pela Hewlett-Packard (hoje Micro Focus, depois da aquisição da divisão de software da HPE) e é amplamente utilizado em grandes corporações que precisam validar aplicações complexas, que muitas vezes envolvem uma mistura de sistemas legados, aplicações web modernas e até integrações entre diferentes tecnologias. O grande diferencial do UFT sempre foi a abrangência tecnológica: ele suporta automação em aplicações desktop, web, SAP, Oracle, PeopleSoft, aplicações client-server, APIs e até mobile, tudo em uma única plataforma, reduzindo a necessidade de várias ferramentas diferentes.
9. Ranorex: é uma ferramenta comercial de automação de testes que ganhou bastante destaque justamente por tentar simplificar e unificar a automação em diferentes tipos de aplicações, cobrindo desde softwares desktop, sistemas web até aplicativos mobile. Ele é muito utilizado em ambientes corporativos onde existe a necessidade de automatizar aplicações complexas, que muitas vezes envolvem tecnologias legadas misturadas com plataformas modernas. Seu grande diferencial é oferecer uma interface visual poderosa, que permite a criação de testes sem exigir conhecimento avançado em programação, mas ao mesmo tempo dar liberdade para quem domina código escrever scripts mais sofisticados em linguagens como C# e VB.NET, já que o Ranorex é baseado no ecossistema .NET.
10. Visual Studio Test Professional: é uma edição do Visual Studio voltada especificamente para gestão e execução de testes de software dentro do ecossistema da Microsoft. Ele não é apenas um IDE, mas uma suíte de ferramentas pensada para equipes de QA e de desenvolvimento que precisam trabalhar de forma integrada em projetos com práticas ágeis e DevOps. Enquanto versões como o Visual Studio Enterprise são mais amplas e englobam todo o ciclo de desenvolvimento, o Test Professional é um produto direcionado para o ciclo de vida de testes, oferecendo recursos de planejamento, acompanhamento e execução.
11. TestingWhiz: é uma ferramenta de automação de testes voltada principalmente para equipes que buscam praticidade e velocidade na criação de testes, sem depender fortemente de programação. Diferente de frameworks open source como Selenium, que exigem bastante conhecimento técnico e montagem de infraestrutura, o TestingWhiz aposta em uma abordagem codeless, baseada em uma interface visual intuitiva onde o testador pode construir fluxos de teste arrastando e configurando blocos de ações pré-definidas. Isso o torna bastante atraente para equipes de QA funcionais ou para empresas que querem introduzir automação de forma rápida, sem exigir que todo o time saiba programar.
Existem muitas ferramentas de testes automatizados disponíveis para diversas linguagens de programação e tipos de testes. Aqui estão algumas das mais populares, categorizadas por seu propósito principal:
1. **Frameworks de Teste Unitário**:
- **JUnit**: Framework de testes unitários para Java.
- **NUnit**: Framework de testes unitários para .NET.- **PyTest**: Framework de testes para Python.
- **Mocha**: Framework de testes para JavaScript e Node.js.
- **RSpec**: Framework de testes para Ruby.- **TestNG**: Outro framework de testes para Java.
2. **Ferramentas de Teste de Integração e Funcional**:
- **Selenium**: Automação de navegadores para testes de aplicações web.
- **Cypress**: Ferramenta de teste de front-end para aplicações web modernas.
- **Protractor**: Ferramenta de teste de end-to-end para aplicações Angular.- **Watir**: Ferramenta de automação de testes para aplicações web.
3. **Ferramentas de Teste de Interface de Usuário (UI)**:
- **Appium**: Framework de automação para aplicações móveis (iOS e Android).
- **TestComplete**: Ferramenta de automação de testes para aplicações desktop, web e móveis.
- **Ranorex**: Ferramenta de automação de testes para desktop, web e dispositivos móveis.4. **Ferramentas de Teste de Performance e Carga**:
- **JMeter**: Ferramenta para testes de carga e performance.
- **Gatling**: Ferramenta de teste de carga focada em aplicações web.
- **LoadRunner**: Ferramenta de teste de carga e performance da Micro Focus.5. **Ferramentas de Teste de Segurança**:
- **OWASP ZAP**: Ferramenta para testes de penetração de aplicações web.
- **Burp Suite**: Ferramenta de teste de segurança para aplicações web.
- **Acunetix**: Ferramenta de varredura de segurança para aplicações web.6. **Ferramentas de Teste de APIs**:
- **Postman**: Ferramenta para teste de APIs RESTful.
- **SoapUI**: Ferramenta de teste para serviços web SOAP e REST.
- **RestAssured**: Biblioteca para teste de APIs REST em Java.7. **Ferramentas de Integração Contínua**:
- **Jenkins**: Ferramenta de integração contínua que pode ser usada para executar testes automatizados.
- **GitHub Actions**: Serviço de integração e entrega contínua integrado ao GitHub.- **GitLab CI/CD**: Ferramenta de integração contínua e entrega contínua do GitLab.
- **CircleCI**: Serviço de integração contínua e entrega contínua.
8. **Ferramentas de Análise de Código e Cobertura de Testes**:
- **SonarQube**: Ferramenta de análise estática de código que também mede a cobertura de testes.
- **JaCoCo**: Ferramenta de cobertura de testes para Java.
- **Cobertura**: Ferramenta de cobertura de testes para Java.
- **Istanbul**: Ferramenta de cobertura de testes para JavaScript.
Essas ferramentas ajudam a automatizar diferentes tipos de testes, desde testes unitários básicos até testes de performance e segurança, garantindo a qualidade e a estabilidade do software durante todo o ciclo de desenvolvimento.
## [QA] Unit testing
![]()
![]()
![]()
![]()
![]()
![]()
Os **testes unitários** ou **testes de unidade** (unit tests) é toda a aplicação de teste nas assinaturas de entrada e saída de um sistema. Consiste em validar dados válidos e inválidos via I/O (entrada/saída) sendo aplicado por desenvolvedores ou analistas de teste (QA). Testes unitários são métodos que verificam o funcionamento de unidades de código, vulgo métodos, e seus objetos associados. O grande objetivo, por incrível que pareça, não é ter uma grande cobertura, e sim resultar em uma arquitetura melhor, menos acoplada, e de melhor manutenção. Classes com muitas depêndencias são muito difíceis de testar. Métrica utilizada: cobertura de código.
Portanto, são testes que verificam se uma parte específica do código, costumeiramente a nível de função, está funcionando corretamente. Em um ambiente orientado a objetos (OOP) é usualmente a nível de classes e a mínima unidade de testes inclui construtores e destrutores.
> 🧪 Os testes de unidade verificam unidades, como métodos, funções e componentes dentro do software. São os testes mais rápidos, baratos de escrever e sua manutenção é simples. Para verificar o comportamento dessas pequenas partes isoladas do sistema sem dependências externas como banco de dados, APIs, arquivos ou rede. Por isso, eles são rápidos de executar, baratos de manter e oferecem feedback imediato durante o desenvolvimento. Como testam unidades isoladas, são fundamentais para garantir a estabilidade do código à medida que ele evolui.
Uma **unidade** (unit) é a menor parte testável de um programa de computador, no programação procedural uma unidade pode ser uma função individual ou um procedimento do nosso código, imagine que toda função é uma pequena fábrica que fabrica alguma coisa que pode sair, sem a necessidade de entrar algo. Idealmente, cada teste de unidade é independente dos demais, o que possibilita ao programador testar cada módulo isoladamente.
Relação de conceitos de testes de unidade:
**I/O Input-Output** (Entrada e Saída): são todas as entradas e saídas existentes na programação. Portanto, os testes de unidade servem para front-end e back-end. Eles são uma prática essencial no desenvolvimento de software, pois ajudam a garantir a qualidade do código e a facilitar a manutenção. Os testes de unidade são realizados em pequenas unidades de código, como funções, componentes ou módulos. Eles são projetados para testar a funcionalidade e a lógica dessas unidades de forma isolada. Isso significa que os testes de unidade não dependem de outros componentes ou módulos para funcionar.
No **front-end**, os testes de unidade são usados para testar a funcionalidade e a lógica de componentes de interface do usuário, como botões, formulários e listas. Eles também são usados para testar a interação entre componentes.
No **back-end**, os testes de unidade são usados para testar a funcionalidade e a lógica de serviços, APIs e outros componentes de back-end. Eles também são usados para testar a integração entre componentes de back-end.
Os testes de unidade oferecem uma série de benefícios, incluindo:
- Aumento da qualidade do código: Os testes de unidade ajudam a identificar erros e bugs no código antes que eles sejam integrados ao sistema. Isso resulta em um código mais confiável e estável.
- Facilidade de manutenção: Os testes de unidade facilitam a manutenção do código, pois permitem verificar se as alterações não afetaram o funcionamento de outras partes do código.
- Agilidade no desenvolvimento: Os testes de unidade permitem que os desenvolvedores tenham mais confiança ao realizar refatorações ou adicionar novos recursos. Isso permite que as equipes desenvolvam de forma mais rápida e eficiente.
Portanto, os testes de unidade são uma prática importante para qualquer desenvolvedor, independentemente da área de atuação.
Sobre os processos de desenvolvimento de software, no terceiro passo no nível de queda do modelo cascata e no quarto passo do modelo RAPID, entramos na parte de codificação e testes unitários. Ou seja, é a construção do sistema em si. Então, só depois de eu entender todo o problema, só depois de eu saber das necessidades e se é possível ou viável para começar a desenvolver. Muitas vezes, para começar a gente se pergunta se é para pegar logo no processo de codificação, ou seja, a desenvolver logo a aplicação o mais rápido possível. No entanto, percebe-se que o tanto de retrabalho que isso gerava, fazia não valer a pena. E fazia com que estourasse muito o orçamento nesse custo. Então, depois deu definir os requisitos, depois de realizar meus modelos de projetos e provar que aquilo é viável, eu então começo o desenvolvimento do meu software em si.
Após o desenvolvimento e junto com o desenvolvimento, entram os testes unitários (unit tests - testes de unidade) que são definidos pelo próprio desenvolvedor onde eles tendem a testar a menor unidade do sistema. Por exemplo: Se eu estou desenvolvendo um sistema de cadastro de cliente, não importa o tipo do sistema (mercadinho, farmácia, padaria, comércio ou de uma grande empresa) e esse desenvolvedor que está escrevendo essas linhas de código de cadastrar um único usuário ou funcionário, por exemplo, ele vai desenvolver um caso de teste para que dado uma entrada (input), ele possa receber uma saída (output) esperada que seria: "usuário cadastrado com sucesso".
Os frameworks de teste de unidade mais populares para **React.js** são:
- Jest: O Jest é um framework de teste de unidade JavaScript criado pelo Facebook. Ele é rápido, fácil de usar e oferece uma variedade de recursos, como testes de snapshot, mocking e asserções.
- Testing Library: A Testing Library é uma biblioteca de utilitários para testes de componentes React. Ela fornece uma API simples e intuitiva que permite testar componentes sem depender dos detalhes de implementação.
- Enzyme: O Enzyme é uma biblioteca de teste de componentes React que fornece uma API poderosa e flexível para manipular o DOM e testar eventos.
A escolha do framework de teste de unidade mais adequado depende das necessidades específicas do projeto. O Jest é uma boa opção para projetos simples, enquanto frameworks como a Testing Library ou o Enzyme podem ser mais adequados para projetos mais complexos.
Os frameworks de teste de unidade mais populares para **Vue.js** são:
- Vue Test Utils: O Vue Test Utils é um conjunto de utilitários para testes de componentes Vue.js. Ele fornece uma API simples e intuitiva que permite testar componentes sem depender dos detalhes de implementação.
- Jest: O Jest é um framework de teste de unidade JavaScript criado pelo Facebook. Ele também pode ser usado para testes de unidade em Vue.js.
- Karma: O Karma é um framework de teste de unidade JavaScript que pode ser usado para executar testes em uma variedade de navegadores. Ele também pode ser usado para testes de unidade em Vue.js.
A escolha do framework de teste de unidade mais adequado depende das necessidades específicas do projeto. O Vue Test Utils é uma boa opção para projetos simples, enquanto frameworks como o Jest ou o Karma podem ser mais adequados para projetos mais complexos.
O framework Python para web back-end chamado **Django** fornece um framework de teste padrão, chamado de **unittest**. Esse framework é baseado na biblioteca padrão unittest do Python e é adequado para testes unitários e de integração.
Além do unittest, existem outros frameworks de teste de unidade disponíveis para Django. Alguns dos frameworks mais populares incluem:
- Pytest: O Pytest é um framework de teste de unidade completo e flexível que oferece uma variedade de recursos, como assertion fixtures, parametrização de testes e testes de desempenho.
- Mock: O Mock é um framework de mocking que permite simular o comportamento de objetos externos. Isso pode ser útil para testar a funcionalidade de componentes que dependem de outros componentes ou APIs externas.
- Selenium: O Selenium é um framework de automação de testes que permite testar a interação com um navegador web. Isso pode ser útil para testar a funcionalidade de componentes de front-end.
A escolha do framework de teste de unidade mais adequado depende das necessidades específicas do projeto. O unittest é uma boa opção para projetos simples, enquanto frameworks como Pytest e Mock podem ser mais adequados para projetos mais complexos.
Sobre os conceitos técnicos a respeito de testes de unidades, temos:
✅ **Testes Válidos** (`pass`): São entradas e saídas de dados comuns ao sistema e pertencem ao processo normal. Não apresentam tratamento além do normal já programado. No caso de retorno deverá seguir os padrões estabelecidos e não permitir retornos fora das regras especificadas. Em testes unitários, estamos nos referindo a casos de teste que exercitam o comportamento correto e esperado da unidade de código sob condições normais (válidas), ou seja: Situações em que tudo ocorre como deveria. São aqueles testes que usam entradas válidas e esperadas, esperam resultados corretos, sem exceções ou erros. Confirmam que o comportamento da função está conforme o esperado.
Características de testes válidos:
| Característica | Exemplo prático |
| --------------------------- | --------------------------------------------------------- |
| Entrada no domínio esperado | CPF válido, número positivo, email formatado corretamente |
| Estado inicial válido | Usuário existente, banco conectado, produto em estoque |
| Fluxo normal do código | Sem exceções, erros, ou retornos inesperados |
| Resultado esperado | Retorno certo, estado alterado corretamente |Exemplo: Teste manual simples em JavaScript - Aqui, `2` e `3` são valores válidos, e o retorno esperado (`5`) confirma o comportamento correto da função.
```javascript
function somar(a: number, b: number): number {
return a + b;
}test("soma dois números positivos", () => {
expect(somar(2, 3)).toBe(5); // ✅ teste válido
});
```
❌ **Testes Inválidos** (`fail`): São entradas e saídas de dados não comuns ao sistema. Apresentam tratamento para validar o tipo de dado inválido ou situação. Pode apresentar até dois retornos, uma mensagem para um log no sistema e uma mensagem com formatação e escrita adequada ao usuário. São tão importantes quanto os testes válidos, porque ajudam a garantir que sua função se defenda bem contra entradas erradas, estados incorretos ou fluxos inesperados. São testes que usam entradas inválidas, incorretas ou fora do esperado; Esperam que o código falhe corretamente (com exceção, erro, ou retorno de falha); Verificam se o sistema é robusto contra dados errados ou uso indevido da função.
Exemplo:
```txt
Dividir (x int,y int)=z int
```Caso tenhamos `x=1` e `y=0`, `z` será um valor com erro e deverá retornar uma mensagem ao usuário, avisando que a operação é inválida. Caso a expressão seja um dado comum do sistema, a autorização para tal validação deverá ser do usuário, pois faz parte do conjunto de regras de negócio. Não existe retorno inválido sem um tratamento. O tratamento genérico será apenas para condições não visíveis na regra e uso do sistema.
👁️🗨️ **Domínio**: No domínio de testes, usamos testes unitários para validar a funcionalidade de cada componente do nosso domínio de negócio, refere-se à parte do sistema que é testada para garantir que a lógica de negócio e a funcionalidade do código estão corretas, os testes unitários focam em componentes individuais desse domínio.
> [!Important]
> É importante ressaltar sobre um termo muito conhecido em ciência da computação, chamado **domínio** (domain), cujo a diferença está no nível de abstração e no foco de cada conceito. A palavra "domínio" realmente aparece em contextos diferentes e pode causar confusão se não for bem delimitada.
>
> No contexto de testes unitários, "domínio" pode se referir genericamente à camada de regras de negócio ou lógica principal da aplicação, que é o alvo ideal desses testes — ou seja, testar a lógica do domínio sem envolver infraestrutura, banco de dados ou interface.
>
> Já em DDD (Domain-Driven Design), "domínio" é o conceito central: é o conhecimento do negócio que está sendo modelado, e tudo gira em torno disso — é a área de interesse do sistema, como logística, financeiro, saúde, etc.
>
> No caso de arquiteturas como Clean Architecture ou Ports & Adapters, "domínio" é uma camada bem definida e isolada que representa as regras puras do negócio, ou seja, aquilo que não muda mesmo que a tecnologia mude; é o núcleo da aplicação.
>
> Já em TDD e BDD, o termo "domínio" aparece implicitamente quando você escreve testes voltados para comportamentos do sistema, especialmente no BDD que foca na linguagem ubíqua e no comportamento esperado do domínio de negócio, enquanto o TDD tende a atuar mais no detalhe técnico e no design emergente.
>
> Por fim, em design de software e design patterns, o "domínio" pode surgir como contexto onde os padrões são aplicados, mas o foco desses conceitos é mais estrutural e de solução técnica do que modelagem de negócio em si.
>
> Então, "domínio" em DDD e arquiteturas limpas é o coração das regras do negócio, enquanto em testes e padrões, é mais o cenário onde você aplica as práticas, muitas vezes sem foco explícito em representar o negócio como um modelo coeso.Você pode dizer:
> "Nos nossos testes de unidade, verificamos se os métodos da entidade `Pedido` calculam corretamente o total do pedido."
Focado na verificação da funcionalidade de unidades isoladas de código (geralmente métodos ou funções), tem como objetivo garantir que cada parte do software funcione conforme esperado de maneira isolada. O contexto aqui é mais técnico e voltado para a qualidade do código e a prevenção de regressões.
Pode ser um campo, uma assinatura, um I/O, ou qualquer tipo de local que receba valores externos ao sistema. Todo domínio deve realizar consistências de dados válidos e inválidos. Um domínio só permite dados com a formatação igual ao que será armazenado.
Ex.: Campo DDD deverá permitir números de até quatro casas não negativas ou a base de dados deve impedir a entrada de valores inválidos. Receber e guardar o mesmo tipo de dado, o tamanho do campo que recebe os dados deve ser menor ou igual ao campo que irá armazenar os dados (em raros casos os campos de armazenamento são menores que os de exibição).
Em suma, domínio é o tipo de valor válido para cada campo. Como exemplo podemos citar:
```txt
Campo nome: Dominio = tipo: string; tamanho:50
```Ao aplicarmos o particionamento por equivalência e a análise por valor limite, poderemos criar as seguintes classes de testes.
Particionamento por Equivalência: campo nome:
- valor em branco (BLANK); Cenário Negativo
- `valor > 50`; Cenário Negativo
- qualquer valor de `1` a `50`; Cenário PositivoAnálise por Valor Limite:
```txt
campo nome: valor em branco; valores 49,50,51;
```Usamos um valor exatamente inferior e exatamente posterior ao valor do campo, devido ao fato dos erros aparecerem nas fronteiras da aplicação.
O domínio de testes unitários, o domínio de DDD (Domain-Driven Design) e o domínio de microsserviços podem estar inter-relacionados, mas não são exatamente o mesmo domínio. Embora o domínio de testes unitários, o domínio de DDD e o domínio de microsserviços não sejam exatamente o mesmo, eles estão inter-relacionados e podem se complementar. Testes unitários verificam a funcionalidade do código, DDD foca na modelagem do domínio de negócios, e microsserviços organizam a aplicação em componentes pequenos e independentes. Quando usados juntos, esses conceitos podem ajudar a criar sistemas robustos, bem projetados e testados. Você pode ter uma comunicação mais assertiva com o seu time falando da maneira proposta acima que eles irão entender de qual tipo de domínio se trata.
Quando você combina esses três conceitos, você pode comunicar algo como:
> "No nosso sistema, utilizamos uma abordagem de Domain-Driven Design (DDD) para modelar nosso domínio de negócio. Cada parte do domínio de negócio é implementada como um microsserviço independente, permitindo escalabilidade e independência de desenvolvimento. Além disso, garantimos a qualidade e a correção da lógica de negócio com testes unitários abrangentes, que validam cada componente do nosso domínio de negócio."
Agora, imagine que você precisa validar o funcionamento de sua aplicação em um determinado cenário, mas este cenário só funciona se integrado com uma aplicação de terceiros específica, a qual você não tem total acesso.
O fato de você não ter acesso traz um dilema: ou você ignora a integração e roda o teste, inviabilizando a obtenção de resultados assertivos, ou não testa de modo algum.
Nenhuma das opções é realmente a escolha ideal, mesmo se você encarar aquele ditado “feito é melhor que perfeito”. Afinal, é muito tênue a linha entre não trazer os resultados corretos e não testar.
O ideal seria existir algo que possibilitasse que os testes trouxessem os melhores resultados, mesmo sem acesso à tecnologia necessária. A boa notícia é que existe! Estamos falando dos _test doubles_.
Os **Test doubles** são objetos usados em testes de software para substituir componentes reais que um sistema ou módulo depende, permitindo que os testes sejam mais controláveis, isolados, rápidos e confiáveis. O nome “double” vem da ideia de um “dublê” no cinema: alguém que substitui o ator em cenas arriscadas. No código, os test doubles substituem partes reais (como um banco de dados, uma API externa ou até um serviço interno) que você não quer ou não pode usar diretamente durante o teste. Termo genérico para qualquer substituição de objeto de produção em testes.
De acordo com Martin Fowler, test doubles é um conceito usado quando, para viabilizar a realização de testes, algum objeto em produção precisa ser substituído por outro.
Quando falamos em Test Doubles, estamos nos referindo a objetos “substitutos” que criamos para simular comportamentos em testes, especialmente quando não queremos ou não podemos usar a implementação real de uma dependência. O nome vem de uma analogia com o “stunt double” do cinema, o dublê que substitui o ator em cenas perigosas. Em testes de software, um test double substitui um componente real para que possamos isolar o código que queremos validar. Fazendo um paralelo simplista, eles são como dublês de atores em um filme: substituem aplicações reais durante a realização de um teste por simular sua aparência e comportamento. Isso traz menos complexidade ao teste, além de permitir verificar uma parte de um sistema sem ficar preso em todas as suas outras porções.
Em resumo, as duplicatas de teste (test doubles) são usadas para criar testes rápidos, independentes, determinísticos e confiáveis. Eles representam componentes reais, semelhante à forma como os dublês são usados nos filmes.
Talvez você não esteja familiarizado com o termo test double. É que, às vezes, eles são generalizados pela palavra mock, ou então pela expressão “mockar” . O fato é que mock é apenas um dos exemplos de test doubles dentro de uma família muito maior. Além dos mocks, existem, por exemplo, os fakes, os stubs, os dummies… Abaixo, explico alguns deles.
Eles são fundamentais para testes automatizados, principalmente testes unitários, e ajudam a focar apenas na lógica que você está testando, sem interferência de outras partes do sistema.
Os tipos mais comuns de Test Doubles são geralmente organizados em cinco categorias clássicas:
- `Dummy`: Passado mas nunca usado; listas de parâmetros de preenchimentos
- `Fake`: tem uma implementação funcional, mas não é adequado para produção
- `Stub`: Fornece respostas prontas para chamadas feitas durante o teste
- `Spy`: Um esboço que registra informações sobre como foi chamado
- `Mock`: Pré-programado com expectativas, formando uma especificação das chamadas esperadasO primeiro é o **Dummy**, que é o mais simples: são objetos criados apenas para preencher parâmetros ou satisfazer assinaturas de métodos, mas que nunca são realmente usados. Eles existem para evitar nulls ou falhas de compilação, mas não participam da lógica do teste.
Os dummies são dados que substituem dados reais, mas que não chegam a ser realmente utilizados no teste. São normalmente usados para satisfazer determinados parâmetros.
Como uso de dummies é possível diminuir a complexidade durante a escrita de um teste, ignorando o que não é relevante no cenário e focando no que realmente importa.
Os **fakes** são test doubles que têm implementações reais diferentes àquelas que existem em produção. é uma implementação funcional simplificada de um componente real. Ele realmente executa algo, mas de maneira controlada e menos complexa.
Um exemplo clássico é um banco de dados em memória usado nos testes em vez de um banco de produção, ou um servidor HTTP falso que responde rapidamente sem precisar de rede.
Os fakes aceleram os testes e evitam dependências externas pesadas. Podemos considerá-los como um “atalho”, algo implementado para deixar a execução do teste mais dinâmica frente ao que é colocado no ar de fato.
Basicamente, não há lógica em um fake — ele retorna um valor determinado por quem o implementa e, justamente por isso, não é um elemento adequado para ir à produção. Contudo, ele elimina a necessidade de implementar uma funcionalidade real, o que seria bem mais complexo.
O **Stub** é um objeto que fornece respostas pré-determinadas para chamadas feitas durante o teste. Ele não tem lógica real, apenas retorna o que foi configurado. Os stubs são muito úteis quando você quer garantir que o teste receba certos dados sem depender de sistemas externos, como simular uma API retornando um JSON fixo.
São similares aos fakes e aos spies, mas, ao contrário destes, ele consegue alterar seu comportamento com base na maneira como ele foi chamado no teste.
O stub também é uma forma de teste duplo usado para fornecer uma resposta controlada das dependências de um componente. Esse tipo de teste duplo pode ser usado para fornecer uma resposta controlada sem fazer nenhuma lógica real. Os stubs geralmente não dão nenhuma resposta fora do que está programado no teste. Isso permite que mais de um cenário para uma única dependência seja representado no teste.
Qual é a diferença entre Mock e Stub? A julgar pela definição de mock e stub, parece que mock e stub são a mesma coisa. Isso ocorre porque uma simulação pode ser considerada uma extensão de um esboço. Um stub é apenas uma implementação simples usada para fornecer uma resposta controlada. Enquanto isso, a simulação não funciona apenas como um stub, mas também verifica o comportamento de um componente e sua interação com o objeto que a simulação cria.
Os **mocks** tem expectativas sobre o jeito que deve ser chamado e, caso ele não seja chamado da forma correta, o teste deve falhar. Eles são usados para testar interações entre métodos e são úteis onde não há como verificar algumas mudanças de estado ou retornos do método testado diretamente.
**Mocking** (Mockado) é uma técnica usada em testes de software para simular o comportamento de dependências externas, como serviços, bancos de dados, ou APIs, dentro de uma unidade de código que você está testando. Ao invés de usar as implementações reais dessas dependências, você cria "mocks" (objetos falsos) que imitam o comportamento esperado, permitindo testar o código de forma isolada. O principal benefício do mocking é garantir que o teste foque apenas no comportamento da unidade de código em questão, sem se preocupar com o comportamento ou estado das dependências externas.
Exemplo: Se você estiver testando um serviço que depende de um repositório de dados, você pode usar um mock para simular as respostas do repositório, em vez de acessar o banco de dados real.
Mocking e testes unitários são diferentes, mas se complementam para testar unidades isoladas do código. Os testes unitários tem o objetivo de testar uma unidade de código (como uma função ou método) de forma independente, garantindo que ela funcione corretamente em diferentes cenários. Em um teste unitário, você se preocupa apenas com o comportamento interno dessa unidade.
Já o mocking é uma técnica usada nos testes unitários para simular (mockar) dependências externas da unidade que está sendo testada. Isso permite que você foque exclusivamente na lógica interna da unidade, sem se preocupar com o comportamento ou estado de serviços, bancos de dados ou APIs reais. Em resumo, os mocks ajudam a garantir que os testes unitários sejam realmente isolados e focados na unidade de código que está sendo testada, sem interferências externas.
Por fim, existe o **Spy**, que é uma espécie de híbrido. Ele é um objeto real, mas que tem a capacidade de registrar as chamadas que recebeu, permitindo inspecionar posteriormente como foi utilizado. Diferente do mock, o spy não necessariamente substitui a lógica real; ele executa de verdade, mas deixa rastros que podem ser verificados.
Um spy age como um espião sob a implementação real e, como o mock, consegue verificar as interações entre os métodos.
A diferença para o mock é que o spy chama a implementação real para todos os métodos da interface mockada, a não ser que para algum método este seja configurado para retornar algo específico diferente da implementação real.
Existem boas práticas bem consolidadas no uso de *test doubles*, e as imagens refletem parte disso. A primeira mostra uma distinção clássica: **mocks (e spies)** são usados mais em **commands** (ações que mudam estado), enquanto **stubs (stubs, dummies, fakes)** são usados em **queries** (consultas que retornam dados). Essa prática vem da ideia de **Command Query Separation (CQS)**, que sugere que comandos devem ter efeitos colaterais e não retornar valores, enquanto queries retornam valores mas não mudam estado. Se você usa stubs para queries, você só garante dados de entrada controlados; já mocks para comandos te permitem verificar se uma ação realmente ocorreu.
Na segunda imagem, vemos os test doubles categorizados em um “universo” com sobreposições. A ideia é reforçar que **Dummy, Stub, Fake, Spy e Mock** são variações de substitutos que usamos em testes, mas cada um tem um propósito específico. A boa prática aqui é **usar o double mais simples possível que resolva o problema do teste**. Por exemplo, se você só precisa preencher um parâmetro, use um dummy. Se precisa de dados controlados, use um stub. Se quer uma implementação leve mas funcional (como um banco em memória), use um fake. Se precisa verificar chamadas, use um mock. Se precisa capturar interações sem substituir toda a lógica, use um spy. O erro comum é usar mocks para tudo, gerando testes frágeis, acoplados demais ao código interno.
Outra boa prática é manter os doubles **próximos do contexto do teste** e não generalizá-los cedo demais. Doubles genéricos compartilhados entre muitos testes podem virar fonte de inconsistência ou dificultar a leitura. Em TDD, por exemplo, é comum criar o double dentro do próprio teste e só refatorar para reaproveitar se realmente houver repetição.
Também é importante lembrar que test doubles são **ferramentas para testes de unidade**, onde o isolamento é crucial. Em testes de integração ou end-to-end, eles devem ser usados com cautela, pois nesses níveis o objetivo é justamente validar a interação real entre componentes. Usar doubles nesses contextos pode dar uma falsa sensação de segurança, porque o código passa no teste, mas falha no ambiente real.
Por fim, existe uma máxima importante: **“don’t mock what you don’t own”** — ou seja, evite criar mocks para dependências externas de terceiros, como APIs de bibliotecas que você não controla. Isso acopla seus testes a detalhes que podem mudar fora do seu controle. Em vez disso, prefira abstrair essas dependências atrás de interfaces próprias e mockar essas interfaces. Isso deixa seus testes mais estáveis e mantém o acoplamento sob seu domínio.
Ou seja, as boas práticas são: usar mocks para comandos e stubs para queries, escolher o double mais simples possível para o cenário, não abusar de mocks em todos os lugares, restringir o uso a testes de unidade, e nunca mockar diretamente dependências externas que você não controla.
## [QA] Integration Testing
![]()
![]()
![]()
![]()
![]()
![]()
![]()

Os **testes de integração** (integration testing) são de um nível mais alto, e testam a relação de elementos, como por exemplo um banco de dados e o software. A realização destes testes é mais lenta, afinal possuem um outro grau de complexidade. É um teste em grupos que valida a integração de um sistema com outros sistemas ou banco de dados, é feito pelo desenvolvedor para validar se existe falha de dados entre integrações nos sistemas e se está com o comportamento correto.
São testes que verificam se _módulos_ diferentes do sistema funcionam corretamente juntos, por exemplo: back-end + banco de dados, API + autenticação, ou microsserviços se comunicando via fila ou HTTP.
No contexto de testes de integração, **módulos** são partes distintas de um sistema que têm responsabilidade própria (ex: autenticação, pagamentos, cadastro), podem ou não ser executadas separadamente e precisam se comunicar entre si para o sistema funcionar corretamente. Módulo, em testes de integração, se refere a qualquer parte autônoma do sistema (seja uma classe, serviço, camada ou microserviço) que precisa se comunicar com outras partes para o sistema funcionar. Testes de integração são o que garantem que essas partes realmente funcionam bem juntas, usando dados reais, banco de dados, APIs, filas, etc. Os testes de integração avaliam se a comunicação entre esses módulos está funcionando como esperado.
Portanto, testes de integração são uma fase do processo de teste de software em que módulos ou componentes são combinados e testados em grupo. Ela sucede o teste de unidade, em que os módulos são testados individualmente, e antecede o teste de sistema, em que o sistema completo é testado num ambiente que simula o ambiente de produção. Os testes de integração têm como objetivo verificar a funcionalidade e a comunicação entre módulos. Eles são projetados para identificar erros de integração, que são erros que ocorrem quando dois ou mais módulos são combinados.
Exemplo em um sistema web: Suponha que você tenha um sistema de e-commerce com:
- `Módulo A`: Autenticação
- `Módulo B`: Catálogo de produtos
- `Módulo C`: Carrinho de compras
- `Módulo D`: Pagamento
No teste de integração, você testaria coisas como:
- ✅ Se um usuário autenticado consegue adicionar produtos ao carrinho (A + C).
- ✅ Se o sistema só libera o pagamento se os produtos forem válidos (B + D).
- ✅ Se o pedido final é registrado no banco com todas as dependências funcionando (A + B + C + D).
Dependendo do contexto, módulo pode significar:
| Contexto | O que é o "módulo"? | Exemplo prático |
| ---------------------- | ------------------------------------------------------------- | --------------------------------------------------------- |
| Back-end (monolito) | Um **componente separado** (serviço, classe, camada) | Módulo de usuários vs módulo de produtos |
| Microserviços | Um **serviço inteiro** com banco próprio e API | Serviço de checkout vs serviço de catálogo |
| Front-end modularizado | Um conjunto de **funções ou hooks reutilizáveis** | Módulo de autenticação + módulo de requisições |
| Arquitetura em camadas | Uma **camada** da aplicação (Controller, Service, Repository) | Controller depende do Repository funcionando corretamente |Então, nos testes de integração, o objetivo é:
- Testar a colaboração entre módulos reais, sem mocks.
- Verificar se integrações entre módulos distintos (por código, HTTP, fila, banco) estão funcionando.
- Detectar problemas de acoplamento, dependência ou contrato entre partes do sistema.Se você quer conhecer todas as principais ferramentas e frameworks de testes de integração, aqui vai um apanhado completo, organizado e atualizado, abrangendo múltiplas linguagens e stacks modernas — incluindo ferramentas de propósito geral, específicas para APIs, banco de dados, E2E (fim a fim), e ambientes complexos como microserviços.
**JavaScript / TypeScript**:
- **Jest** embora seja popular para testes de unidade, também permite testes de integração. Suporte a mocking, spies, e `supertest` para testes de API.
- **Supertest** testa endpoints HTTP diretamente em apps Node.js/Express/Koa. Muito usado com Jest ou Mocha.
- **Mocha + Chai** flexível e modular. Excelente para testes de integração com `chai-http` e `supertest`.
- **Playwright / Puppeteer** usados para testes de integração **em aplicações web** (navegador). Permitem verificar o fluxo do usuário entre frontend e backend.
- **Vitest** suporte moderno e rápido a testes de integração com foco em performance. Alternativa moderna ao Jest com suporte a ES Modules.
Deno com `deno test` embutido no runtime. Suporte nativo a testes de integração (com `fetch`, banco, etc.). Test libraries auxiliares: `assert` (de `std`) e não precisa de Supertest: pode usar `fetch` diretamente no servidor rodando em teste.
**Python**:
- **Pytest** um dos frameworks mais completos. Com plugins como `pytest-django`, `pytest-flask`, `pytest-asyncio`, cobre testes de integração completos.
- **Requests / HTTPX** combinados com `pytest`, são ótimos para testar APIs REST ou FastAPI/Flask/Django.
- **Behave / Lettuce** estilo BDD com testes de integração escritos em Gherkin.
**Java / Kotlin**:
- **JUnit (5+)** com Spring Boot Test: realiza testes de integração com o contexto da aplicação.
- **Testcontainers** executa serviços reais (como PostgreSQL, Kafka, Redis) em containers Docker nos testes.
- **RestAssured** framework fluente para testar endpoints REST em Java.
**Go**:
- **testing + httptest** padrão da linguagem. Cria servidores HTTP fake para testar integração de APIs.
- **GoConvey** framework para escrever testes BDD e de integração.
- **Testcontainers-Go** integra serviços externos com Docker no teste (como bancos, filas, etc).
**Rust**:
- `#[tokio::test]`, `reqwest`, `warp::test` ferramentas nativas para rodar testes async com HTTP, banco etc.
- **Testcontainers-rs** similar ao de outras linguagens: banco de dados real em Docker.
**PHP**:
- **PHPUnit** com Laravel ou Symfony, permite testes de integração completos com banco de dados e API.
- **Codeception** específico para testes de integração e E2E. Robusto e com suporte a múltiplos módulos (HTTP, DB, etc).
**Ruby**:
- **RSpec + Capybara** usado em Rails para testes de integração web.
- **Minitest** leve e nativo do Ruby. Com suporte a integração com bancos e HTTP.
Ferramentas genéricas de integração / multi-linguagem:
- **Postman + Newman** ideal para testar APIs REST/GraphQL com fluxo, autenticação, tokens, etc.
- **Insomnia Tests** alternativa ao Postman com foco em GraphQL e REST.
- 🧪 **Cypress** (E2E com integração real) testa interface, mas também valida backend real.
- 🐳 **Testcontainers** (multi-language: Java, Node, Go, Rust, .NET, etc) executa serviços reais (Redis, MySQL, RabbitMQ) em Docker, durante os testes.
- **WireMock / MockServer**: Simulam serviços externos para testar integração sem dependências reais.
- **Docker Compose** (para orquestrar múltiplos serviços nos testes), muito usado para testes de integração entre microsserviços.
Em casos especiais, testes com fila e mensageria (Kafka, RabbitMQ):
* `Testcontainers` (com RabbitMQ/Kafka)
* `docker-compose` para subir o ambiente
* Bibliotecas nativas da linguagem para consumir/produzirPara testes de integração, você pode optar por:
| Categoria | Ferramentas-chave |
| --------- | ---------------------------------------------- |
| Node.js | Jest + Supertest, Mocha, Vitest |
| Deno | `deno test` + `fetch/assert` |
| Python | Pytest + HTTPX + Testcontainers |
| Java | JUnit + SpringBootTest + Testcontainers |
| Multi | Postman/Newman, Testcontainers, Docker Compose |
| E2E web | Playwright, Cypress, Puppeteer |
| APIs | RestAssured, Insomnia, HTTPX, Supertest |## [QA] Functional Testing
Testes de um nível ainda maior, são os **functional tests**, que testam o sistema completo e garante a correção de funcionalidades no ponto de vista do cliente.
O que é importante pensarmos é no tempo de execução de testes que teremos. Os testes de unidade existem desde o início do projeto, qualquer commit deveria ser acompanhada por um teste.
É comum que o desenvolvedor que queria concluir um projeto rapidamente deixe de fazer testes para otimizar o tempo. Como resolver esse impasse? Antes do commit, devemos executar todos os testes, embora saibamos que isso é em um plano ideal, e muitas vezes desnecessário dependendo da modificação que foi realizada. Até mesmo executar todos os testes unitários pode ser complicado.
Uma técnica comum é executar o que chamamos de **smoke tests**. Na prática, trata-se de **uma seleção de testes que garantem que as funcionalidades mais importantes do sistema estejam operando corretamente**. Esses testes avaliam um conjunto menor de elementos, por isso são mais rápidos, e dessa maneira teremos a garantia de que o software está operante em sua estrutura básica. Depois disso, podemos aplicar todos os testes e garantir uma varredura maior de erros.
Em resumo, devemos observar a categoria de cada teste; em ambientes diferentes fazer escolhas de desempenho e que melhor atendam nossa demanda; aplicar boas práticas de testes ( testes isolados, legíveis, expressivos); realizar testes na parte de build e adquirir feedbacks o mais rápido o possível.
## [QA] Regression testing
O **teste de regressão** é uma técnica do teste de software que consiste na aplicação de versões mais recentes do software, para garantir que não surgiram novos defeitos em componentes já analisados. Regression testing, ou teste de regressão, é um tipo de teste de software realizado para garantir que mudanças recentes no código, como correções de bugs, novas funcionalidades ou outras modificações, não introduzam novos defeitos ou causem falhas em partes já existentes do software. Este processo é essencial para manter a integridade do software após qualquer tipo de alteração.
Em resumo, regression testing é uma prática crucial no desenvolvimento de software que visa assegurar que novas mudanças não comprometam funcionalidades existentes, contribuindo para a estabilidade e qualidade contínua do sistema.
Objetivos do Regression Testing:
1. **Verificar Estabilidade**: Assegurar que as novas mudanças não impactaram negativamente o comportamento existente do software.
2. **Identificar Regressões**: Detectar rapidamente qualquer falha que possa ter sido introduzida devido a mudanças recentes.
3. **Manter Qualidade**: Garantir que o software continua funcionando conforme esperado, mantendo a qualidade e a confiabilidade.
Quando Realizar Regression Testing:
- **Após Correções de Bugs**: Sempre que um bug é corrigido, é importante garantir que a correção não tenha introduzido novos problemas.
- **Após Adição de Novas Funcionalidades**: Novas funcionalidades podem afetar o funcionamento existente, por isso testes de regressão são necessários.
- **Durante Refatoração de Código**: A refatoração melhora o design interno do código, mas pode inadvertidamente introduzir erros.
- **Em Atualizações de Dependências**: Mudanças em bibliotecas ou frameworks subjacentes podem afetar o comportamento do software.
Métodos de Regression Testing:
1. **Reexecução Completa**: Executar todos os testes existentes para garantir que o software inteiro funcione corretamente. Isso é muitas vezes impraticável para grandes sistemas devido ao tempo e recursos necessários.
2. **Seleção de Casos de Teste**: Escolher um subconjunto de testes que são mais relevantes para as áreas do código que foram alteradas.
3. **Teste de Prioridade**: Focar nos testes mais críticos e mais propensos a serem afetados pelas mudanças.
4. **Automação de Testes**: Usar ferramentas de automação para executar testes de regressão de forma eficiente e repetível.
Ferramentas Comuns para Regression Testing:
- **Selenium**: Para automação de testes de interface de usuário em aplicações web.
- **JUnit/NUnit/PyTest**: Para testes unitários automatizados.
- **Jenkins/GitLab CI**: Para integração contínua e execução automatizada de testes.
- **Robot Framework**: Para testes automatizados em diversos contextos.
- **TestNG**: Para organização e execução de testes em Java.
Benefícios do Regression Testing:
- **Detecção Precoce de Defeitos**: Permite a identificação e correção rápida de novos problemas.
- **Redução de Riscos**: Minimiza o risco de introdução de erros ao modificar o software.
- **Qualidade Contínua**: Mantém a qualidade do software ao longo de todo o ciclo de desenvolvimento.
Desafios do Regression Testing:
- **Manutenção de Testes**: Manter um conjunto de testes atualizado pode ser desafiador à medida que o software evolui.
- **Tempo e Recursos**: A execução de um grande conjunto de testes pode ser demorada e consumir muitos recursos.
- **Falsos Positivos/Negativos**: Pode haver casos onde testes falham ou passam erroneamente, exigindo investigação adicional.
## [QA] E2E - End-to-end
![]()
![]()
![]()
![]()
![]()
![]()
O **E2E - End-to-end** refere-se a um tipo de teste ou processo que envolve a verificação de um sistema ou fluxo de trabalho em sua totalidade, desde o início até o fim, simulando as condições reais de uso pelo usuário final. Em resumo, "end-to-end" se refere à abordagem de teste que abrange todo o sistema ou processo, do início ao fim, para garantir seu funcionamento correto e eficaz.
O E2E verifica se todos os componentes de um sistema (front-end, back-end, bancos de dados, APIs externas, etc.) funcionam juntos conforme esperado, do início ao fim. Em um contexto de desenvolvimento de software, os testes end-to-end são realizados para garantir que todas as partes do sistema estejam funcionando corretamente juntas, desde a interface do usuário até o backend, incluindo integrações com outros sistemas, se aplicável. Isso é feito para garantir que o sistema esteja se comportando conforme o esperado e atendendo aos requisitos do usuário final.
Os testes end-to-end são frequentemente usados para validar fluxos de trabalho completos em um aplicativo ou site, simulando a interação do usuário final com o sistema. Eles podem envolver a automação de cliques de mouse, preenchimento de formulários, navegação entre páginas e verificação de resultados. No entanto, a validação é um conceito que se aplica a múltiplos níveis de teste, incluindo o E2E, mas não é exclusiva dele.
As principais ferramentas para testes *E2E (End-to-End)* e soluções complementares (como Zod) para validação de dados, organizadas por contexto de uso:
Ferramentas Especializadas em E2E:
1. **Para Aplicações Web**
- **Cypress**: Framework completo para testes E2E em navegadores, com suporte a simulação de interações (cliques, formulários) e debug em tempo real. Inclui **Cypress Testing Library** para boas práticas de seleção de elementos.- **Playwright**: Suporta múltiplos navegadores (Chromium, Firefox, WebKit) e linguagens (JS/TS, Python, .NET). Recursos como auto-wait, gravação de testes e testes em paralelo. O Playwright é uma biblioteca de automação de código aberto para testes de navegador e web scraping desenvolvida pela Microsoft e lançada em 31 de janeiro de 2020, que desde então se tornou popular entre programadores e desenvolvedores web.
- **Selenium**: Mais antigo, mas ainda usado em projetos legados. Requer mais configuração (WebDriver).
3. **Para APIs**
- **Supertest** (Node.js): Biblioteca para testar APIs HTTP integrada ao Jest/Mocha. Valida status codes, responses e headers.
- **Postman/Newman**: Coleções de requisições podem ser automatizadas como testes E2E (com scripts em JavaScript).4. **Para Mobile**
- **Appium**: Framework open-source para testes E2E em aplicativos Android/iOS.
- **Detox**: Focado em React Native e aplicações nativas, com suporte a sincronização automática.5. **Para Desktop**
- **Spectron** (para Electron): Integra Selenium com o Electron para testar aplicações desktop.Ferramentas Complementares (como Zod):
1. **Validação de Dados em Testes E2E**
- **Zod**: Valida esquemas de respostas de API ou estados da UI durante testes. Exemplo:
```typescript
const LoginResponseSchema = z.object({ token: z.string() });
const data = LoginResponseSchema.parse(await response.json());
```
- **Joi**: Similar ao Zod, mas mais usado em back-end (Node.js) para validar objetos.2. **Mock de Dados/APIs**
- **MSW (Mock Service Worker)**: Intercepta requisições HTTP em testes E2E para simular APIs sem depender do back-end real.- **JSON Server**: Cria uma API fake baseada em um arquivo JSON para testes iniciais.
4. **Asserções Avançadas**
- **Jest/Vitest**: Oferecem matchers (como `.toMatchObject()`) para validar estruturas de dados em testes.- **Chai**: Biblioteca de asserções para Mocha, com sintaxe legível (ex.: `expect(user).to.have.property('name')`).
6. **Monitoramento e Relatórios**
- **Allure Report**: Gera relatórios visuais detalhados de testes E2E.- **Sentry**: Captura erros em tempo real durante testes (útil para debug em CI/CD).
Exemplo de Fluxo com Ferramentas Combinadas
1. **Playwright** simula um usuário fazendo login.
2. **MSW** mocka a API de login (opcional).
3. **Zod** valida se a resposta da API contém `{ token: string }`.
4. **Allure Report** gera um dashboard com os resultados.Quando Usar Cada Uma?
- **Testes de UI Completa**: Cypress/Playwright.
- **APIs**: Supertest + Zod/Joi.
- **Mobile**: Appium/Detox.
- **Validação de Dados**: Zod (TypeScript) ou Joi (JavaScript).
- **Mock**: MSW ou JSON Server.Essas ferramentas podem ser combinadas para cobrir todos os aspectos de testes E2E, desde a interação do usuário até a integridade dos dados.
## [QA] TDD - Test-Driven Development











O **TDD - Test-Driven Development**, ou em português "Desenvolvimento guiado por testes" ou Desenvolvimento Orientado a testes ou Desenvolvimento digirido por testes, é uma técnica de desenvolvimento de software que se relaciona com o conceito de verificação e validação e se baseia em um ciclo curto de repetições: Primeiramente o desenvolvedor escreve um caso de teste automatizado que define uma melhoria desejada ou uma nova funcionalidade. Logo, é produzido código que possa ser validado pelo teste para posteriormente o código ser refatorado para um código sob padrões aceitáveis.
Basicamente, ela ajuda a aumentar a produtividade a partir de testes já consolidados. O TDD (Test-Driven Development) foca em um tipo específico de teste chamado teste de unidade. No entanto, ele pode influenciar outros tipos de testes durante o ciclo de desenvolvimento.
Kent Beck, considerado o criador ou o 'descobridor' da técnica, declarou em 2003 que TDD encoraja designs de código simples e inspira confiança. Desenvolvimento dirigido por testes é relacionado a conceitos de programação de XP - Extreme Programming, iniciado em 1999, mas recentemente tem-se criado maior interesse pela mesma em função de seus próprios ideais. Através de TDD, programadores podem aplicar o conceito de melhorar e depurar código legado desenvolvido a partir de técnicas antigas.
> O TDD é considerado uma técnica ou metodologia, muito adotada nos times de desenvolvimento. Isso porque ele é direcionado ao desenvolvimento de softwares. Contudo, pelo fato de inverter a ordem dos trabalhos – do teste para o código – é um pouco impopular entre os Devs. No entanto, após pegar o jeito, o desenvolvimento ganha um up, e a técnica traz muitos resultados positivos ao projeto.
> [!Important]
> O livro **Test-Driven Development: By Example** do Kent Beck, é considerado a obra fundamental sobre TDD (Test-Driven Development). Ele é importante por alguns motivos históricos e práticos. Primeiro, porque o Kent Beck foi um dos criadores originais do TDD como prática estruturada dentro do movimento Extreme Programming (XP) e um dos autores do Manifesto Ágil. Ou seja, ele não só ajudou a popularizar o conceito, como também foi quem deu a forma mais clara e prática para sua aplicação. Segundo, porque esse livro é didático e prático: Beck explica a ideia de TDD usando exemplos pequenos, progressivos e reais. Ele mostra o famoso ciclo do TDD — Red, Green, Refactor — de maneira muito detalhada, quase como se estivesse pair programming com o leitor. Ele começa com problemas bem simples, como implementar uma calculadora de dinheiro com operações básicas, e vai aumentando a complexidade, até chegar em casos mais sofisticados.O TDD segue a lógica do ciclo: Red, Green e Refactor. Este ciclo é uma abordagem estruturada para escrever e melhorar código de software de maneira incremental, garantindo que ele seja testável, funcional e de alta qualidade. Aqui está uma explicação detalhada de cada fase do ciclo:
🔴 **Red**: Escreva um teste que apresenta erros, que falhe. Ação: você começa escrevendo um teste automatizado para a funcionalidade que deseja implementar. Este teste é baseado nos requisitos e especificações do que o código deve fazer. Resultado: O teste falha, pois a funcionalidade ainda não foi implementada. A falha confirma que o teste é válido e que a funcionalidade não existe no momento.
🟢 **Green**: Logo após, escreva um código que passe no teste, que funcione e faça o teste passar. Ação: Escrever a quantidade mínima de código necessário para fazer o teste passar. Nesta fase, o foco está em implementar a funcionalidade de maneira rápida e simples, sem se preocupar muito com a qualidade ou elegância do código. Resultado: O teste passa, indicando que a funcionalidade básica foi implementada corretamente.
🟡 **Refactor**: Depois disso, "refatorar" o que foi feito, ou seja, eliminar a redundância e melhorar a qualidade do código, ou seja, melhorar e otimizar o código sem alterar sua funcionalidade, mantendo todos os testes passando. Ação: Refatorar o código escrito na fase anterior para torná-lo mais limpo com princípios de Código Limpo (Clean Code), eficiente e fácil de manter. Isso pode incluir a remoção de duplicações, melhoria da legibilidade, e conformidade com padrões de design. Resultado: O código é melhorado sem alterar seu comportamento externo. Os testes (novos e antigos) continuam passando, garantindo que a funcionalidade permanece correta após as melhorias.
A **Refatoração** é o processo de reestruturar o código de um software para melhorar sua qualidade interna, sem alterar seu comportamento externo. A principal finalidade da refatoração é tornar o código mais limpo, legível, e fácil de manter, otimizando aspectos como desempenho, organização e modularidade.
Ela costuma envolver a remoção de duplicação de código, simplificação de estruturas complexas, e melhoria na nomenclatura de variáveis, classes e funções, além de aplicar padrões de design e princípios como o SOLID.
Refatorar também ajuda a prevenir a "dívida técnica", que ocorre quando decisões de design ou implementação apressadas criam problemas futuros. Em metodologias ágeis, a refatoração é geralmente integrada ao processo de desenvolvimento contínuo, sendo realizada entre ciclos de implementação de novas funcionalidades. Portanto, a refatoração se encaixa como uma prática regular dentro da fase de desenvolvimento, especificamente na etapa de Integração Contínua (CI), que foca na qualidade do código e na automação de testes.
> [!Important]
> Por mais de vinte anos, programadores experientes no mundo inteiro contaram com o livro **Refatoração: Aperfeiçoando o Design de Códigos Existentes** de Martin Fowler para aperfeiçoar o design de códigos existentes e melhorar a manutenibilidade do software, assim como para deixar o código existente mais fácil de entender. Essa nova edição ansiosamente esperada foi atualizada por completo para refletir mudanças vitais no domínio da programação. Refatoração 2ª edição contém um catálogo atualizado das refatorações e inclui exemplos de código JavaScript bem como novos exemplos funcionais que demonstram a refatoração sem classes. Assim como na edição original, este livro explica o que é refatoração, por que você deve refatorar, como reorganizar um código que precise de refatoração e como fazer isso de forma bem-sucedida, independentemente da linguagem usada.Após ler este livro, você será capaz de:
- Entenda o processo e os princípios básicos da refatoração;
- Aplique rapidamente refatorações convenientes para deixar um programa mais fácil de entender e de alterar;
- Reconheça “maus cheiros” no código que sinalizam oportunidades para refatorar;
- Explore as refatorações, cada uma com suas explicações, a motivação, o mecanismo e exemplos simples;
- Escreva testes robustos para suas refatorações;
- Reconheça as contrapartidas e os obstáculos para a refatoração.O Desenvolvimento dirigido por testes requer dos desenvolvedores criar testes automatizados que definam requisitos em código antes de escrever o código da aplicação. Os testes contém asserções que podem ser verdadeiras ou falsas. Após as mesmas serem consideradas verdadeiras após sua execução, os testes confirmam o comportamento correto, permitindo os desenvolvedores evoluir e refatorar o código. Normalmente todos os testes são efetuados de forma continua de acordo com o desenvolvimento cada funcionalidade criada deve ser acompanhada de um teste bem descrito e projetado, então deve-se escolher a área do projeto ou requisitos da tarefa para melhor orientar o desenvolvimento destes testes.
Desenvolvedores normalmente usam frameworks de testes, como **xUnit**, para criar e executar automaticamente uma série de casos de teste.
> As empresas esperam que seus colaboradores sejam realmente muito bons em testes unitários e a melhor forma de garantir isso é pedindo TDD. Muitas pessoas aprendem testes de forma muito superficial, mas um profissional que já praticou TDD em alguma codebase real tem uma vantagem sobre os outros, pois já enfrentou diversos problemas e sabe como contorná-los.
Os testes em integração contínua são sobre **feedback do software**, como a maioria dos métodos ágeis. Feedback é o ponto chave para um desenvolvimento com qualidade, seja ele a nível técnico, de gestão ou pessoal. O Feedback é o ponto chave para um desenvolvimento com qualidade, seja ele a nível técnico, de gestão ou pessoal.
Bom, muito provavelmente não fui eu quem inventou o nome Ciclo de Feedback para desenvolvimento de Software mas estou adicionando o guiado a Testes. Legal mas o que isso quer dizer? Quer dizer que, quando trabalhando no desenvolvimento de uma tarefa qualquer, que seja guiada a testes, nós temos que trabalhar em cima do feedback que os testes nos trazem e não com o pensamento de que temos apenas que codar a `feature` e adicionar testes para garanti-las. Realizar uma tarefa guiada a testes com esse pensamento é disperdiçar boa parte do potencial da abordagem do TDD.

Então, o TDD, BDD e DDD são três abordagens diferentes que se complementam no desenvolvimento de software. O **TDD (Test-Driven Development)** é uma prática focada no ciclo de escrita de testes antes da implementação do código. A ideia central é escrever primeiro um teste que falha porque a funcionalidade ainda não existe, depois escrever o código mínimo necessário para fazê-lo passar e, em seguida, refatorar para manter a qualidade. Esse ciclo de “red, green, refactor” garante que o sistema nasça já com testes cobrindo a funcionalidade, evita sobrecarga de bugs e mantém o design do código limpo e orientado ao que realmente precisa ser implementado. Com o tempo, você ganha confiança para refatorar e evoluir a aplicação porque sabe que os testes automatizados garantem que nada quebrou.
Já o **BDD (Behavior-Driven Development)** nasceu como uma evolução do TDD, com foco maior na comunicação entre time de negócios e desenvolvimento. A ideia é descrever o comportamento esperado do sistema em linguagem natural, geralmente em formato de cenários (“Dado que”, “Quando”, “Então”), o que torna os testes legíveis até para quem não programa. Isso aproxima desenvolvedores, QA, product owners e até clientes, porque todos falam a mesma língua sobre o que o software deve fazer. Enquanto o TDD guia o código a partir de testes técnicos, o BDD guia o código a partir do comportamento esperado do usuário ou do negócio, trazendo clareza e diminuindo ambiguidades nos requisitos.
O **DDD (Domain-Driven Design)**, por sua vez, é mais abrangente, é uma forma de estruturar todo o sistema a partir do domínio de negócio. O ponto central é que o design da aplicação deve emergir do entendimento profundo do problema que se quer resolver, criando um modelo de domínio claro, organizado e rico, que reflete a realidade do negócio. DDD traz conceitos como entidades, agregados, value objects, bounded contexts e ubiquitous language, para garantir que o código represente fielmente as regras do domínio e que diferentes partes do sistema conversem de forma coerente. Ele não é uma técnica de testes como TDD ou BDD, mas sim uma filosofia arquitetural que ajuda a manter a complexidade sob controle e tornar o software mais alinhado às necessidades reais do negócio. Por isso, o DDD é amplamente adotado em Code Reviews.
Quando você junta as três abordagens, tem um fluxo bastante poderoso: o DDD te ajuda a entender e modelar corretamente o domínio; o BDD garante que os comportamentos mais importantes estejam claros e validados com o negócio; e o TDD dá a base técnica para implementar cada parte com qualidade e segurança. Assim, você cobre desde a concepção do sistema até a implementação e os testes, reduzindo riscos, aumentando a clareza e facilitando a manutenção e evolução ao longo do tempo.
Esse diagrama mostra o **ciclo do TDD** expandido para além do “red-green-refactor” clássico, conectando os diferentes níveis de testes (caixa preta, cinza e branca) dentro do ciclo de desenvolvimento. Destrinchando o que ele está representando:
À esquerda, temos a **Black Box**, que é onde entram os **testes de aceitação**. Eles validam se o sistema, como um todo, faz o que o usuário ou o negócio espera. É aqui que entra aquele “Dado–Quando–Então” do BDD, por exemplo.
1. Esse é o primeiro passo (1): escrever o teste de aceitação que vai falhar porque o sistema ainda não implementa a funcionalidade.
2. Na sequência (2), entra a parte de **testes funcionais**, ainda numa visão de alto nível. Eles descrevem como cada funcionalidade deve se comportar dentro do sistema. É a ponte entre aceitação e código.
3.4 Indo para a direita, temos a **White Box**, com **testes unitários**. Esse é o núcleo do TDD clássico: para cada pequena parte do código (função, método, classe), você escreve um teste que falha (3), implementa o código mínimo para passar (4) e depois refatora (ciclo interno de unidade).
5. Depois de escrever e passar os testes unitários, você volta para os **testes funcionais** (5), garantindo que aquela unidade está contribuindo corretamente para o comportamento esperado.
6. Daqui em diante, surgem os **testes de integração** (6), que validam se as diferentes partes do sistema trabalham bem juntas (por exemplo, se um serviço conversa com outro via API, se a camada de aplicação integra corretamente com o banco).
6.7 Os testes funcionais e de integração se retroalimentam (6 e 7), porque quando você conecta módulos, é comum ajustar tanto o comportamento funcional quanto a integração.
8. Por fim, quando tudo isso passa, você retorna para os **testes de aceitação** (8). Se eles agora passam, significa que o sistema como um todo está atendendo ao que foi pedido. Se ainda falham, o ciclo recomeça até que os critérios de aceitação estejam cumpridos.
Resumindo: o ciclo mostra que o TDD não vive só no nível unitário (teste–código–refatora), mas pode ser entendido como um **encadeamento de ciclos em diferentes camadas**: começa com aceitação (visão do usuário), vai para funcional (visão do sistema), integrações (módulos se falando) e finalmente unidades (blocos de código). É um processo iterativo que desce do mais abstrato até o mais concreto e depois sobe de volta, validando em todos os níveis.
## [QA] BDD - Behavior-Driven Development
          
O **BDD - Behavior-Driven Development** (Desenvolvimento Orientado a Comportamento), é uma metodologia de desenvolvimento ágil que tem como foco a colaboração entre desenvolvedores, QA (Quality Assurance) e partes interessadas não técnicas para criar uma compreensão compartilhada do comportamento desejado de um software. O BDD é uma evolução do TDD (Test-Driven Development) e adiciona uma ênfase maior na comunicação e na clareza dos requisitos.
Em resumo, o BDD promove uma abordagem colaborativa para o desenvolvimento de software, focando em comportamentos e resultados esperados do sistema, o que ajuda a garantir que o software entregue atenda às necessidades reais dos usuários e stakeholders.
Aqui estão os componentes chave do BDD:
1. **Foco no Comportamento**: Em vez de se concentrar apenas na implementação técnica e nos testes de unidade, o BDD foca em como o software deve se comportar sob várias condições, incluindo o comportamento do usuário final.
2. **Linguagem Ubíqua (Ubiquitous Language)**: Utiliza uma linguagem comum (frequentemente baseada em linguagens naturais como o inglês) que pode ser compreendida por todos os membros da equipe, incluindo desenvolvedores, QA, e stakeholders não técnicos. Isso ajuda a reduzir ambiguidades e garantir que todos tenham a mesma compreensão dos requisitos. A Linguagem Ubíqua (Ubiquitous Language) é um conceito central no Design Orientado a Domínio (DDD) que visa criar uma linguagem comum entre todos os envolvidos em um projeto, seja para os especialistas no domínio, desenvolvedores, ou mesmo os usuários finais. Essa linguagem comum facilita a comunicação e colaboração, reduzindo a possibilidade de mal-entendidos e melhorando a qualidade do desenvolvimento.
3. **Especificações Executáveis**: No BDD, os requisitos são escritos em forma de especificações que podem ser executadas como testes. Essas especificações geralmente seguem um formato estruturado, como Gherkin que é uma linguagem de domínio específico usada para descrever comportamentos esperados de um sistema de forma clara e compreensível por todos os envolvidos no desenvolvimento de software, incluindo pessoas não técnicas, que usa palavras-chave como "`Given`" (Dado), "`When`" (Quando), e "`Then`" (Então) para descrever cenários de teste:
- `Given` (Dado): Descreve o contexto inicial ou o estado do sistema antes de uma ação específica.
- `When` (Quando): Descreve a ação ou evento que ocorre.
- `Then` (Então): Descreve o resultado esperado ou o comportamento do sistema após a ação.
Exemplo: Login no Sistema
[](#)
```gherkin
Feature: Login no SistemaScenario: Login com credenciais válidas
Given: o usuário está na página de login
When: o usuário insere suas credenciais válidas
Then: o usuário é redirecionado para a página inicial
```Exemplo 2: Pesquisar produto
[](#)
```gherkin
Funcionalidade: Pesquisar produto
Eu como cliente
Quero fazer pesquisas no site da OLX
Para buscar por produtosCenário: Buscar produto com sucesso
Dado que estou no site da OLX como um comprador de SP
Quando eu fizer uma busca por um produto
Então serão exibidos os resultados de busca para o produto em SP
```5. **Ferramentas de BDD**: Existem várias ferramentas que suportam BDD, ajudando a automatizar as especificações executáveis. Algumas das ferramentas populares incluem Cucumber (para várias linguagens como Java, Ruby), SpecFlow (para .NET), Behave (para Python), entre outras.
5. **Benefícios do BDD**: Uma das maiores virtudes do BDD - Behavior-Driven Development é unir os dois mundos TDD e DDD, de fato é representar uma interseção entre TDD (Test-Driven Development) e DDD (Domain-Driven Design), pois ele nasce da necessidade de alinhar o desenvolvimento técnico com o entendimento do negócio, garantindo que o software reflita o comportamento esperado do sistema a partir da perspectiva do usuário ou domínio. BDD pode ser visto como a interseção onde a clareza de intenção do domínio (trazida pelo DDD) se encontra com a prática de testar antes de desenvolver (como propõe o TDD).
- **Melhor Comunicação**: Facilita a comunicação entre todos os membros da equipe, garantindo que todos entendam os requisitos de maneira clara e compartilhada.
- **Desenvolvimento Orientado a Valor**: Foca no que realmente importa para os usuários finais e stakeholders, ajudando a priorizar o desenvolvimento de funcionalidades de maior valor.
- **Menos Retrabalho**: Reduz ambiguidades nos requisitos, diminuindo o risco de desenvolvimento de funcionalidades incorretas ou desnecessárias.
- **Documentação Viva**: As especificações atuam como uma documentação viva que está sempre em sincronia com o comportamento atual do sistema.
## [QA] ATDD - Acceptance Test-Driven Development
O **ATDD - Acceptance Test-Driven Development** é uma prática e uma variação do desenvolvimento orientado a testes (TDD) que coloca o **teste de aceitação** como ponto central do ciclo. Enquanto no TDD tradicional o desenvolvedor escreve primeiro testes unitários para depois implementar o código que os satisfaz, no ATTD o processo começa com a definição dos testes de aceitação, geralmente descritos em linguagem mais próxima do negócio, representando os critérios que o sistema precisa cumprir para ser aceito pelo cliente ou pelo usuário final.
A ideia é alinhar desde o início o que será construído com o que realmente tem valor para o negócio. Os testes de aceitação funcionam como especificações executáveis: descrevem cenários, entradas, saídas e comportamentos esperados do sistema em termos que os stakeholders entendem. Depois disso, os desenvolvedores implementam o código necessário para fazer esses testes passarem. Em muitos casos, ferramentas como Cucumber, SpecFlow ou Behave são usadas para escrever cenários em Gherkin (“Given, When, Then”), permitindo que as próprias partes interessadas consigam validar e até revisar os testes.
Na prática, o ATTD acaba funcionando como uma ponte entre BDD e TDD. Ele compartilha com o BDD a preocupação de usar exemplos de negócio como base para o desenvolvimento, mas mantém a disciplina do TDD de usar testes automatizados como motor do ciclo. Isso ajuda a garantir que o software entregue não só funcione tecnicamente, mas também atenda ao valor esperado pelos usuários, reduzindo retrabalho e mal-entendidos.
## [QA] SDD - Specification-Driven Development
O **SDD - Specification-Driven Development** é uma abordagem de desenvolvimento de software em que o ponto de partida e o guia principal de todo o ciclo de criação é a especificação formal e clara do sistema a ser construído.
Em vez de começar direto pela implementação de código ou até mesmo pelos testes, a ideia central do SDD é produzir especificações bem definidas que podem ser documentos estruturados, contratos formais, modelos de comportamento ou até DSLs (linguagens específicas de domínio) e a partir delas orientar o design, os testes e a implementação.
No desenvolvimento tradicional, o código vem primeiro, seguido pela documentação, testes e explicação.
Mas no desenvolvimento orientado a especificações, invertemos o roteiro. A especificação se torna a fonte da verdade e o código flui a partir dela. Com a IA no circuito, esse fluxo de trabalho se torna ainda mais poderoso. Gera especificações a partir da intenção do usuário. Deriva código a partir de especificações e valida a lógica com linguagem natural.
Neste tópico, você aprenderá como fluxos de trabalho com tecnologia de IA permitem sistemas mais rápidos, seguros e modulares, especialmente para aplicações embarcadas e distribuídas na borda. O desenvolvimento orientado a especificações começa definindo o que o sistema deve fazer.
Antes de tocar no código, as especificações podem delinear o comportamento funcional, relacionamentos de entrada/saída, casos extremos (edge cases), failure cases, Temporal logic e.g. do x, y, and z.
O propósito é reduzir ambiguidades que frequentemente aparecem em métodos tradicionais, onde requisitos em linguagem natural muitas vezes dão margem a interpretações diferentes por desenvolvedores, testadores e stakeholders. No SDD, a especificação não é apenas um requisito no papel, mas sim um artefato executável ou validável, que pode ser usado para gerar código, validar regras de negócio automaticamente ou servir de referência inequívoca para testes.
Dessa forma, o SDD busca criar um fluxo em que a especificação é a fonte da verdade. Em muitas implementações dessa prática, as especificações podem ser escritas em formatos que permitam serem executadas ou verificadas, como contratos formais, modelos lógicos ou até arquivos que depois geram código de suporte, documentação ou casos de teste. Isso cria uma forte ligação entre o que o cliente espera, o que o time desenvolve e o que é testado. Diferente do TDD (Test-Driven Development), que coloca os testes como guia para a implementação, ou do BDD (Behavior-Driven Development), que enfatiza a escrita de cenários de comportamento em linguagem quase natural, o SDD é mais rígido e sistemático, focando na **especificação técnica ou formal** como ponto de convergência, de forma que a implementação não seja “interpretada” pelos desenvolvedores, mas derivada da especificação.
Essa abordagem é especialmente útil em domínios onde erros de interpretação podem ser críticos, como sistemas financeiros, governamentais, industriais e médicos, porque minimiza a distância entre o que foi pedido e o que é entregue. Além disso, o SDD facilita auditorias, compliance e rastreabilidade, já que a especificação pode ser usada tanto como documento de contrato entre as partes quanto como mecanismo técnico para validar que o software está aderente às regras. Por outro lado, aplicar SDD de forma efetiva exige disciplina, ferramentas adequadas e equipes acostumadas a lidar com formalismo maior do que em metodologias mais flexíveis, o que pode ser uma barreira em times ágeis que preferem rapidez de iteração.
## [QA] DDD - Domain-Driven Design
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
O **DDD - Domain-Driven Design** (Projeto Orientado a Domínio) é uma abordagem de desenvolvimento de software que se concentra em entender o **domínio do negócio** e modelar o software em torno desses conceitos e regras de negócio.
É um tipo de modelagem de software e um design de software orientado a objetos (OOP) que procura reforçar conceitos e boas práticas relacionadas à OOP e surgiu como uma resposta às dificuldades enfrentadas por desenvolvedores ao lidarem com sistemas complexos, especialmente em domínios de negócio onde a lógica e os requisitos mudam frequentemente. Isso vem em contrapartida com o uso comum do Data-Driven Design (Projeto Orientado a Dados), que a maioria dos desenvolvedores usa sem mesmo ter consciência disso.
O DDD nos permite planejar uma arquitetura de microsserviços decompondo o sistema maior em unidades independentes, compreendendo as responsabilidades de cada uma e identificando seus relacionamentos, ele não é um design pattern específico, mas sim uma importante abordagem de design de software, com foco na modelagem de software para corresponder a um **domínio** de acordo com as informações dos especialistas desse domínio. O Domain-Driven Design (DDD) surgiu como uma metodologia revolucionária para a modelagem de software, desenvolvida com o intuito de refinar e otimizar a correspondência entre o design do software e o domínio do software e o domínio do problema que ele busca resolver.
> Os microsserviços são a forma mais escalável de desenvolver software. Mas você precisa de um bom design que permita que as equipes de desenvolvedores trabalhem de forma autônoma e implementem sem atrapalhar umas às outras, caso contrário, você perderá os benefícios de escalabilidade. O DDD ajuda a delimitar responsabilidades claras entre os serviços, o que permite que equipes atuem de forma independente e coordenada.
> [!Important]
> Foi popularizado por Eric Evans em seu livro **Domain-Driven Design: Tackling Complexity in the Heart of Software**, publicado em 2003. Esse livro não é leve, especialmente se você ainda está no início da jornada. Ele exige uma certa base em desenvolvimento orientado a objetos (OOP), arquitetura de software e experiência prática com projetos reais. Geralmente, ele é mais proveitoso depois que você já trabalhou em sistemas mais complexos ou com arquitetura em camadas.O DDD acaba aparecendo muito em code reviews (revisões de código) porque ele não é só um conjunto de padrões técnicos, mas uma maneira de organizar o raciocínio e o design do sistema a partir do domínio de negócio. Diferente de TDD e BDD, que são mais voltados ao como testar e como validar comportamento, o DDD toca no como estruturar o código para refletir a realidade do problema que a aplicação resolve.
Num code review, o time não está apenas olhando se o código “funciona”, mas se ele está legível, sustentável e alinhado ao domínio. É aí que DDD se torna um guia. Por exemplo, quando você define entidades e value objects, o revisor consegue avaliar se você está representando o domínio corretamente ou se misturou regras de negócio com detalhes de infraestrutura. Se você trabalha com bounded contexts, o code review ajuda a garantir que cada módulo está respeitando suas fronteiras e não está acoplando responsabilidades que deveriam estar separadas. E quando se usa a linguagem ubíqua, qualquer pessoa envolvida no projeto pode bater o olho no código e identificar termos familiares do negócio, reduzindo ambiguidades.
Isso muda o tom da revisão: em vez de ser só “esse método está mal nomeado” ou “esse if pode virar um switch”, passa a ser “essa entidade realmente pertence a este contexto?” ou “essa regra deveria estar no domínio ou no serviço de aplicação?”. São discussões de mais alto nível, que evitam dívida técnica e fortalecem a coerência do sistema. Por isso, times que adotam DDD costumam ter code reviews mais ricos, que não param na forma, mas questionam se a essência do código está fiel ao problema que ele resolve.
Sem levar em conta o DDD, as **técnicas de modelagem de domínio** são métodos utilizados na engenharia de software para compreender e representar o domínio de um problema específico. O domínio refere-se à área de conhecimento, contexto ou setor de negócios em que o software está sendo desenvolvido. A modelagem de domínio tem como objetivo capturar os conceitos, regras e relacionamentos do domínio em um formato compreensível e utilizável pelos desenvolvedores. Então, o DDD (Domain-Driven Design) é uma abordagem para o desenvolvimento de software que combina conceitos de design de software e técnicas de modelagem de domínio. Não é considerado um design pattern específico, mas sim uma abordagem geral para projetar e estruturar sistemas de software. Domain-Driven Design (DDD) é um método de design de software em que os desenvolvedores constroem modelos para entender os requisitos de negócios de um domínio. Esses modelos servem como base conceitual para o desenvolvimento de software.
No entanto, suas raízes vêm de práticas e ideias que estavam sendo discutidas na indústria desde os anos 1990. Durante esse período, muitas empresas estavam adotando metodologias ágeis e enfrentando problemas ao construir sistemas que não apenas funcionassem, mas que também fossem fáceis de entender, modificar e expandir. Um dos grandes desafios era a chamada "lacuna semântica" entre os especialistas de domínio (pessoas que entendem o negócio) e os desenvolvedores (que implementam soluções técnicas). Essa lacuna frequentemente levava a softwares que funcionavam de forma errada ou que eram difíceis de adaptar a mudanças nos requisitos.
A ideia inicial do DDD é voltar à uma modelagem OO mais pura, por assim dizer. Devemos esquecer de como os dados são persistidos e nos preocupar em como representar melhor as necessidades de negócio em classes e comportamentos (métodos). Isso significa que em DDD um `Cliente` pode não ter um *setter* para os seus atributos comuns, mas pode ter métodos com lógica de negócio que neste domínio de negócio pertencem ao `cliente`, como `void associarNovoCartao(Cartao)` ou `Conta recuperarInformacoesConta()`. Em resumo, as classes modeladas e os seus métodos deveriam representar o negócio da empresa, usando inclusive a mesma nomenclatura. A persistência dos dados é colocada em segundo plano, sendo apenas uma camada complementar.
O DDD nasceu da necessidade de aproximar esses dois mundos. Eric Evans observou que o software bem-sucedido em contextos complexos era construído em torno de um **modelo de domínio** que capturava com precisão o conhecimento do negócio. Ele também percebeu que os sistemas mais sustentáveis utilizavam linguagens comuns entre especialistas e desenvolvedores, além de técnicas para isolar a complexidade e tornar o código mais alinhado com as regras do domínio.
Com a evolução do desenvolvimento de softwares e no aumento da complexidade dos requisitos da aplicação, é extremamente relevante definirmos uma comunicação clara entre as várias partes envolvidas em um projeto de software. É bastante comum haver conflitos entre os termos técnicos utilizados pelas diferentes áreas, seja entre analistas de negócios, desenvolvedores, especialistas financeiros ou de vendas. Nada mais natural haja visto que as equipes estão cada vez mais multidisciplinares. Para auxiliar em uma comunicação fluida, os conceitos do DDD — Domain Driven Design, propõem como um dos seus pilares a definição de uma _Linguagem Ubíqua_.
O DDD formalizou essas práticas ao introduzir conceitos como **Ubiquitous Language** (Linguagem Ubíqua), o cerne do DDD, que promove a criação de uma linguagem compartilhada entre todas as partes interessadas, e **Bounded Contexts** (Contextos Limitados), que ajudam a dividir sistemas grandes e complexos em partes menores e mais compreensíveis. Além disso, o DDD trouxe atenção para padrões arquiteturais que dão suporte ao domínio, como **Entidades** (Entity), **Agregados** (), **Repositórios** () e **Serviços de Domínio** (), estabelecendo um design centrado na lógica de negócios em vez de nas tecnologias subjacentes.
Para que possamos iniciar uma compreensão acerca de Linguagem Ubíqua (Ubiquitous Language), podemos nos valer da análise semântica do termo ubíquo: `u-bí-quo` (latim _ubiquus_, -a, -um), adjetivo:
- Que está ao mesmo tempo em toda a parte. = `ONIPRESENTE`
- Que tem dom da ubiquidade. = `ONIPRESENTE`
- Que está difundido em todo o lado. = `GERAL, UNIVERSAL`.De forma conceitual, a linguagem ubíqua é o conjunto de termos e inter-relações que fornecem a semântica da comunicação do domínio, que reflete a visão do negócio. E de forma prática, ao se trabalhar com DDD, entende-se como comunicação de mesma linguagem, em um único modelo, de forma que todos os envolvidos no projeto tenham a mesma compreensão acerca dos termos utilizados. Linguagem ubíqua pode parecer um termo complexo de se compreender, mas outro termo também utilizado para identificar este tipo de comunicação, nos auxilia em uma melhor compreensão: _Linguagem Onipresente_.
**Linguagem Onipresente** é essencialmente os termos, palavras e definições utilizadas por todo o domínio do projeto. É o idioma utilizado no cotidiano da empresa, as terminologias da realidade do negócio. Quando um projeto não respeita a **linguagem do domínio** diversos problemas de comunicação surgem, dificultando o desenvolvimento, implantação e sustentação da solução.
Quando termos utilizados no projeto vão sendo traduzidos, de acordo com o uso em cada departamento, a comunicação se torna anêmica e a assimilação do conhecimento disperso. Em seu livro Domain Driven Design — Atacando as Complexidades no Coração do Software, Eric Evans descreve de maneira clara esta problemática:
> O custo de toda a tradução, além do risco de entendimento errado, é simplesmente muito alto. Um projeto precisa de uma linguagem em comum que seja mais robusta que o mínimo denominador comum.
A Linguagem Onipresente (ubiquitous language) não está limitada a diagramas em UML (Unified Modeling Languages, ou Linguagem de Modelagem Unificada), mas principalmente, define em seu vocabulário nome das classes e operações de destaque. Inclui regras para implantar um dicionário uniforme e explícito para o modelo, para que o mesmo possa ser utilizado com o máximo de eficiência possível.
O **Diagram as code** (Diagrama como código) é uma abordagem de criação de diagramas que utiliza código, em vez de ferramentas gráficas, para desenhar e manter diagramas. Essa abordagem permite que os diagramas sejam criados e atualizados utilizando linguagens de programação ou marcadores, em vez de ferramentas de desenho gráfico. A prática de "Diagram as code" (Diagrama como código) pode auxiliar no Domain-Driven Design (DDD), pois é uma prática útil no DDD que ajuda a manter a documentação atualizada, facilita a colaboração, fornece controle de versão e permite a geração automática de diagramas. Vantagens do "Diagram as code" no DDD:
1. **Melhor documentação**: Com o "Diagram as code", você pode manter a documentação do seu modelo de domínio atualizada e sincronizada com o código. Isso ajuda a garantir que a documentação seja precisa e refletida nas mudanças no código.
2. **Modelagem colaborativa**: O "Diagram as code" permite que os membros da equipe colaborativamente trabalhem no modelo de domínio, tornando mais fácil para os desenvolvedores, especialistas em domínio e outros stakeholders discutirem e refinarem o modelo.
3. **Versão e controle**: Com o "Diagram as code", você pode usar sistemas de controle de versão (como Git) para rastrear as alterações no modelo de domínio. Isso ajuda a garantir que todas as alterações sejam documentadas e possam ser revertidas se necessário.
4. **Geração automática de diagramas**: Muitas ferramentas de "Diagram as code" permitem que você gere diagramas automaticamente a partir do código. Isso pode economizar tempo e reduzir a chance de erros manuais.
5. **Integração com o ciclo de desenvolvimento**: O "Diagram as code" pode ser integrado ao ciclo de desenvolvimento de software, permitindo que os desenvolvedores trabalhem no modelo de domínio em paralelo com o desenvolvimento do código.As ferramentas permitem que você crie diagramas como código, utilizando sintaxes específicas para desenhar os diagramas. Em seguida, elas geram imagens ou diagramas a partir do código. Algumas ferramentas populares para "Diagram as code" incluem:
- Mermaid
- **PlantUML**
- Graphviz
- C4 (abordagem de modelagem de software)Normalmente os especialistas de um domínio, diretores, administradores, analistas, técnicos, possuem pouca familiaridade com o jargão técnico utilizado no desenvolvimento de software, mas utilizam os jargões próprios de sua área de atuação. A partir desta realidade, os especialistas de um domínio descrevem superficialmente o que necessitam, fazendo com que desenvolvedores criem abstrações que sustentem o design da aplicação. Com isso, uma compreensão uniforme vai se deteriorando exponencialmente.
Como solução a esta dispersão na comunicação, devemos usar a linguagem baseada no modelo de forma exaustiva até que a comunicação seja fluida e compreensível entre os diversos setores envolvidos no projeto. Para que possamos alcançar esta fluência, os especialistas do domínio devem vetar termos ou estruturas que não transmitam uma compreensão clara acerca das funcionalidades envolvidas; os desenvolvedores devem se empenhar no cuidado com ambiguidades ou inconsistências que possam corromper o modelo proposto. Ou seja, é um esforço conjunto entre todos os envolvidos, mas, é essencialmente necessário que ocorra.
Como identificar os especialistas de domínio? Especialistas de domínio são os profissionais envolvidos no dia a dia da operação, nos mais diferentes setores, ou seja, são os “conhecedores” do negócio (stakeholders). Normalmente estes especialistas são analistas, técnicos, engenheiros, podendo ser todo aquele que possui a compreensão acerca do fluxo de operação da empresa. Os especialistas de domínio detém o conhecimento sobre as necessidades e requisitos necessários para o processamento das atividades organizacionais.
Em essência, o DDD surgiu para enfrentar a complexidade inerente ao desenvolvimento de software em domínios desafiadores, permitindo que os sistemas sejam projetados de forma que o código seja uma expressão direta das regras e processos do negócio. Com o tempo, o DDD ganhou popularidade e passou a ser usado em diversos contextos, especialmente em sistemas corporativos onde o domínio de negócio é complexo e sujeito a constantes mudanças.
Para o sucesso de um projeto de software, o DDD sugere que tanto especialistas de domínio quanto desenvolvedores devem falar a mesma língua.
A figura acima, ilustra a existência de termos que só os especialistas de domínio conhecem e apresentam expressões somente de caráter tecnológico, os quais são de uso apenas do time de desenvolvimento. Contudo, é necessário que exista um conjunto de termos que devem ser de conhecimento universal, no que se refere ao domínio da aplicação, formando a Linguagem Ubíqua do sistema. A definição de uma linguagem onipresente objetiva principalmente dois propósitos:
- Possibilitar uma comunicação fluida entre os membros de equipes multidisciplinares; Nomear elementos do código da aplicação, como classes, métodos, variáveis, funções, módulos, tabelas de bancos de dados, rotas de APIs, etc.
- Ademais a padronização na comunicação propõe elucidar o significado dos termos, de um forma simples, objetiva e compreensível para facilitar os relacionamentos e associações entre todos módulos necessários.
Qualquer pessoa técnica contribuindo para o modelo deve programar, pelo menos tocar no código, independente do papel desempenhado no projeto. Um responsável por mudar o código deve sempre aprender a expressar o modelo através do código. Todo desenvolvedor deve estar envolvido na discussão sobre o modelo e ter contato com os especialistas do domínio. (EVANS, 2016).
Em seu livro Implementando Domain Driven Design, Vaughn Vernon, pontua que um especialista de domínio tem uma forte influência sobre a linguagem utilizada, devido ao maior conhecimento acerca do negócio, que no final é o contexto imperativo de todo projeto. Estes especialistas tendem a ser influenciados pelos padrões da indústria, contudo, uma linguagem universal deve ser centrada em como o próprio negócio pensa e opera. Ou seja, cada empresa possui seu próprio domínio acerca da execução de seus processos.
Não entenda Linguagem Ubíqua como um conjunto de jargões de negócios sendo impostos ao time de desenvolvimento, e nem mesmo uma sobreposição de termos técnicos sobre o contexto de negócio, mas sim, uma linguagem real que é criada por toda a equipe e que é propagada por toda a corporação.
Compreende-se que haverá discordâncias em relação aos termos utilizados e que estão na mente dos especialistas, mas, a partir do uso aberto da linguagem, a evolução é natural e consolidada por este processo de maturação da comunicação.
O DDD enfatiza a compreensão profunda do domínio do problema e o uso de uma linguagem ubíqua compartilhada entre as equipes de desenvolvimento e especialistas do domínio. Ele propõe a organização do código em torno do domínio do problema, separando-o dos detalhes técnicos e infraestrutura.
Embora o DDD não seja um design pattern em si, ele pode ser combinado com vários design patterns e princípios de design, como Agregado, Repositório, Especificação, Event Sourcing, entre outros. O DDD fornece diretrizes e conceitos para ajudar na criação de uma arquitetura de software robusta e flexível.
Portanto, podemos dizer que o DDD é uma abordagem de design e uma metodologia de modelagem que pode ser aplicada em diferentes arquiteturas de software, como arquitetura em camadas, arquitetura hexagonal, arquitetura de microsserviços, entre outras. Ele fornece princípios e práticas para projetar e estruturar o código em torno do domínio do problema, visando um modelo de domínio rico, desacoplamento e flexibilidade.
É uma abordagem mais ampla para o design de software que abrange vários conceitos e técnicas. DDD enfatiza a modelagem do domínio, a colaboração entre especialistas do domínio e desenvolvedores, e a criação de um código baseado em um entendimento profundo do domínio do problema.
No contexto do DDD, existem design patterns específicos que são frequentemente utilizados para ajudar a implementar os conceitos e princípios do DDD. Alguns desses padrões incluem:
1. Agregado: Se refere a um padrão de design que agrupa um conjunto de objetos relacionados em uma única unidade coesa. O Agregado é uma das principais construções utilizadas para modelar e organizar o domínio em DDD;
2. Repositório: Fornece uma interface para acessar coleções de objetos agregados, permitindo que o domínio permaneça livre de preocupações com persistência. Ele atua como uma camada intermediária entre o domínio e a fonte de dados (como bancos de dados).
3. Serviço de Domínio: Representa operações ou ações do domínio que não pertencem naturalmente a uma única entidade ou value object. Encapsula lógica de negócio que depende de múltiplos objetos.
4. Value Object: Objetos que não possuem identidade própria e são definidos apenas por seus atributos. São imutáveis e usados para representar conceitos como dinheiro, coordenadas ou medidas.
5. Entidade: Objetos do domínio que possuem identidade própria (geralmente um ID) e um ciclo de vida distinto. Diferente de value objects, entidades podem mudar seus atributos ao longo do tempo.
6. Factory: Padrão responsável por encapsular a lógica de criação complexa de objetos, especialmente agregados. Evita a poluição do construtor com lógica de montagem de objetos.
7. Especificação: Define regras de negócio reutilizáveis e combináveis para verificar se um objeto atende a determinados critérios. É útil para separação de responsabilidades e clareza das regras de domínio.
8. Event Sourcing: Técnica onde o estado do sistema é determinado por uma sequência de eventos (ao invés de snapshots de dados). Permite reconstruir o estado do sistema e ter um histórico detalhado das mudanças.
9. Injeção de Dependência (DI - Dependency Injection): Técnica que permite desacoplar componentes do sistema, facilitando testes, manutenção e extensibilidade. No DDD, é comum para injetar repositórios, serviços de domínio e unidades de trabalho nos agregados e serviços de aplicação.
Esses padrões, juntamente com outros conceitos e técnicas, podem ser aplicados para construir uma arquitetura que segue os princípios do DDD. O DDD, portanto, não é um design pattern em si, mas uma abordagem que pode ser implementada usando diversos padrões de design específicos.
Quando não usar DDD? Às vezes só é necessário um CRUD! DDD não é uma solução para tudo. A maioria dos sistemas possui uma boa parte composta por cadastros básicos (CRUD) e não seria adequado usar DDD para isso.
O DDD deve ajudar na modelagem das classes mais importantes e mais centrais do sistema de forma e diminuir a complexidade e ajudar na manutenção das mesmas, afinal este é o objetivo dos princípios de orientação a objetos.
Outro ponto é sobre nós desenvolvedores estarmos compartilhando dados com outros sistemas, as rotinas de integração que recebem ou disponibilizam dados para outros sistemas não devem ser "inteligentes". Muitos desenvolvedores acabam modelando suas classes de negócios tentando resolver as questões internas do sistema e, ao mesmo tempo, pensando em como essas classes serão expostas para outros sistemas. Padrões como DTO (Data Transfer Object) que usam objetos "burros" são mais adequados para isso.
Portanto, o DDD não tenta resolver todos os problemas de todas as camadas de um sistema. Seu foco é na modelagem das entidades principais de negócio usando a linguagem adequada daquele domínio para facilitar a manutenção, extensão e entendimento. Particularmente, eu não seguiria à risca o padrão, até porque existem inúmeros padrões e variações de modelagem OO. Estude os princípios por detrás desses padrões, pois eles são geralmente parecidos e veja o que funciona melhor para cada projeto.