8

Testes de Software

Introdução8.1

Hoje você vai ver por que testar não é uma etapa final de conferência, mas parte da própria construção do software.

Quando uma equipe deixa os testes para o fim, quase sempre descobre os problemas tarde demais. O código já cresceu, várias partes dependem umas das outras, a correção fica mais cara e, pior, ninguém sabe ao certo se a falha está na lógica de negócio, na integração entre módulos, nos dados de entrada ou no entendimento do requisito. É exatamente para reduzir esse risco que a engenharia de software trata testes como uma atividade sistemática.

Segundo Sommerville, o teste tem dois objetivos diferentes e igualmente importantes. O primeiro é mostrar que o software faz o que deveria fazer. O segundo é revelar situações em que ele se comporta de forma incorreta, indesejada ou diferente do especificado. Em outras palavras, parte dos testes serve para validar comportamento esperado, e outra parte serve para tentar expor defeitos.

Nesta aula, vamos focar em três ideias centrais:

  1. onde os testes unitários entram no processo de desenvolvimento;
  2. como funciona o TDD ou desenvolvimento dirigido por testes;
  3. como os testes de aceitação verificam se o sistema está pronto para uso do ponto de vista do cliente.

Também vamos deixar muito claro um ponto que costuma gerar confusão. Teste unitário não é "o teste do sistema inteiro". Ele é apenas um dos níveis de teste. Você ainda pode ter testes de componente, integração, sistema, release e aceitação.

Reflita

Se uma função isolada passa em todos os testes unitários, isso já prova que o sistema completo está pronto para entrega?

Não. Isso prova apenas que aquela unidade, dentro dos cenários cobertos, se comportou como esperado. Ainda faltam as interações entre partes, o comportamento do sistema como um todo, os requisitos não funcionais e, por fim, a verificação de que o cliente realmente considera o software aceitável.

O que os testes realmente respondem8.2

Uma maneira simples de entender testes é pensar que cada nível responde a uma pergunta diferente.

Nível de teste Pergunta principal
Unitário Esta função, método ou classe pequena faz o que deveria fazer?
Integração / componente Essas partes funcionam corretamente quando trabalham juntas?
Sistema O sistema completo se comporta como esperado?
Release Esta versão está boa o suficiente para sair da equipe de desenvolvimento?
Aceitação O cliente considera que o sistema está pronto para uso real?

Sommerville organiza esse assunto mostrando que os testes de desenvolvimento cobrem unidades, componentes e sistema, enquanto testes de release e testes de usuário acontecem em outro contexto e com outro objetivo.

Isso importa porque muita gente aprende pytest, escreve alguns testes e conclui que "o sistema está testado". Não está. Na prática, o que normalmente acontece é o seguinte:

  1. você valida unidades pequenas com rapidez;
  2. depois precisa verificar a comunicação entre partes;
  3. depois precisa validar o comportamento do sistema inteiro;
  4. por fim, ainda precisa confirmar que aquilo atende ao uso real e aos critérios do cliente.

Nesta aula, o foco técnico será em teste unitário, TDD e aceitação. Sempre que eu disser que uma função passou no teste, entenda isso como "passou no nível de teste que está sendo executado", não como garantia absoluta sobre o sistema inteiro.

Testes de desenvolvimento e o lugar do teste unitário8.3

No capítulo 8, Sommerville trata os testes de desenvolvimento como aqueles realizados pela própria equipe de desenvolvimento. O objetivo aqui é, principalmente, encontrar defeitos cedo.

Durante o desenvolvimento, o teste pode acontecer em três níveis:

  1. teste unitário, quando testamos uma unidade pequena, como uma função, um método ou uma classe;
  2. teste de componente ou integração, quando juntamos unidades e verificamos suas interfaces;
  3. teste de sistema, quando o sistema inteiro ou uma parte grande integrada é testada como um todo.

O teste unitário é o mais barato para começar e o mais rápido para executar. Ele é útil porque isola uma regra pequena e permite descobrir defeitos antes que eles se espalhem. Se você tem uma função que calcula juros, aplica desconto ou valida uma entrada, faz muito sentido testá-la de forma independente.

Mas o teste unitário tem limite. Ele não garante, por exemplo:

  1. que a API conversa corretamente com o banco;
  2. que duas classes integradas trocaram dados do jeito certo;
  3. que a interface mostra o resultado correto para o usuário;
  4. que o sistema está pronto para aceite contratual.

O que um bom teste unitário tenta fazer8.3.1

Um bom teste unitário normalmente tenta verificar três coisas:

  1. o comportamento normal esperado;
  2. casos de borda;
  3. entradas inválidas ou situações excepcionais.

Essa ideia conversa diretamente com Sommerville quando ele discute a escolha de casos de teste. Não basta testar apenas o caminho feliz. Você precisa escolher casos que confirmem o funcionamento esperado e também casos que tenham chance de revelar defeitos.

Estrutura básica de um teste automatizado8.3.2

Sommerville resume um teste automatizado em três partes:

  1. configuração, em que você prepara o estado inicial e as entradas;
  2. chamada, em que executa a unidade sob teste;
  3. afirmação, em que compara o resultado obtido com o esperado.

Em pytest, isso aparece de forma muito natural.

Exemplo 1

Vamos começar com uma função simples de desconto.

def calcular_desconto(valor: float, percentual: float) -> float:
    if valor < 0:
        raise ValueError("valor nao pode ser negativo")
    if percentual < 0 or percentual > 100:
        raise ValueError("percentual invalido")

    return valor * (percentual / 100)

Agora os testes com pytest.

import pytest

from desconto import calcular_desconto


def test_calcula_desconto_de_10_porcento():
    resultado = calcular_desconto(200.0, 10.0)
    assert resultado == 20.0


def test_desconto_zero_retorna_zero():
    resultado = calcular_desconto(200.0, 0.0)
    assert resultado == 0.0


def test_lanca_erro_para_valor_negativo():
    with pytest.raises(ValueError):
        calcular_desconto(-50.0, 10.0)


def test_lanca_erro_para_percentual_maior_que_100():
    with pytest.raises(ValueError):
        calcular_desconto(100.0, 120.0)

O que esse conjunto de testes está cobrindo?

  1. um caso normal de uso;
  2. um caso de borda simples, com desconto zero;
  3. erro em valor inválido;
  4. erro em percentual inválido.

Repare que o teste não está preocupado com banco de dados, interface ou API. Isso é intencional. Aqui, queremos isolar a regra da função.

Como executar em Python com uv e pytest8.3.3

Se você quiser um fluxo moderno e simples, pode usar uv para criar o ambiente e instalar o pytest.

uv venv
source .venv/bin/activate
uv pip install pytest
pytest

Uma estrutura mínima de projeto pode ficar assim:

projeto/
├── desconto.py
└── tests/
    └── test_desconto.py

E o comando para rodar os testes pode ser simplesmente:

pytest -q

Se quiser executar um arquivo específico:

pytest tests/test_desconto.py -q

Passar em testes unitários não significa que o software inteiro está validado. Significa apenas que as unidades cobertas se comportaram bem nos cenários testados.

Como escolher casos de teste unitário8.4

Sommerville destaca que testar é caro, então escolher bons casos de teste importa muito. Uma estratégia útil é pensar em partições de entrada e limites.

Considere uma função que calcula frete grátis quando o valor do pedido é maior ou igual a R$ 200,00. Há pelo menos quatro grupos interessantes de teste:

  1. pedidos claramente abaixo do limite;
  2. pedido exatamente no limite;
  3. pedidos acima do limite;
  4. entradas inválidas.

Esse raciocínio é melhor do que sair escolhendo números aleatórios. Você está tentando cobrir comportamentos diferentes do sistema, não apenas "rodar alguma coisa".

Exemplo 2

Agora vamos usar parametrize, que ajuda muito quando a ideia é a mesma e apenas os dados mudam.

import pytest


def calcular_frete(total_pedido: float) -> float:
    if total_pedido < 0:
        raise ValueError("total invalido")

    if total_pedido >= 200:
        return 0.0

    return 15.0


@pytest.mark.parametrize(
    "total, frete_esperado",
    [
        (50.0, 15.0),
        (199.99, 15.0),
        (200.0, 0.0),
        (350.0, 0.0),
    ],
)
def test_calcular_frete(total, frete_esperado):
    assert calcular_frete(total) == frete_esperado


def test_nao_aceita_total_negativo():
    with pytest.raises(ValueError):
        calcular_frete(-1.0)

Esse exemplo é pequeno, mas didaticamente muito bom porque mostra um tipo de caso que aparece direto em prova, em entrevista e em projeto real. O defeito mais comum aqui costuma acontecer no limite, quando o programador escreve > em vez de >=.

E os mocks?8.4.1

Sommerville também comenta que, às vezes, a unidade sob teste depende de algo externo, como banco de dados, relógio do sistema ou um serviço remoto. Nesses casos, usar o objeto real pode tornar o teste lento, difícil de preparar ou instável.

Por isso, é comum substituir essa dependência por um objeto controlado para o teste. Em termos modernos, isso é frequentemente chamado de mock, stub ou fake, dependendo do papel que ele exerce.

Se a sua função depende do horário atual, por exemplo, você pode injetar uma função de relógio. Se depende de um repositório, pode passar um objeto falso que retorna dados previsíveis. A ideia é simples: no teste unitário, você quer isolar a unidade.

TDD: escrever o teste antes do código8.5

Sommerville dedica a seção 8.2 ao desenvolvimento dirigido a testes, ou TDD. A ideia central é intercalar incrementos pequenos de código com testes automatizados.

Em vez de pensar assim:

  1. implementar bastante coisa;
  2. testar depois;

no TDD você pensa assim:

  1. escolher um comportamento pequeno;
  2. escrever um teste para ele;
  3. ver o teste falhar;
  4. escrever o mínimo de código para fazê-lo passar;
  5. refatorar com segurança;
  6. repetir.

O ciclo vermelho, verde e azul8.5.1

Na formulação clássica, você costuma ouvir falar em vermelho, verde e refatoração. Em muitas equipes e materiais didáticos, essa terceira etapa é representada pela cor azul, que passa a indicar o momento de limpar, reorganizar e melhorar o código sem mudar seu comportamento.

Nesta aula, vamos usar essa convenção visual:

  1. Vermelho: o teste novo falha, porque a funcionalidade ainda não existe ou ainda está errada.
  2. Verde: você implementa o mínimo necessário para o teste passar.
  3. Azul: você refatora, melhora nomes, remove duplicação e reorganiza o código, mantendo todos os testes verdes.

Essa imagem ajuda muito porque deixa o processo concreto. No TDD, um teste falhando no começo não é fracasso. É parte esperada do processo.

Passo a passo do TDD8.5.2

Com base no Sommerville, o fluxo fica assim:

  1. identificar um incremento pequeno de funcionalidade;
  2. escrever o teste automatizado desse incremento;
  3. executar os testes e observar a falha do teste novo;
  4. implementar a funcionalidade;
  5. executar novamente os testes;
  6. refatorar;
  7. avançar para o próximo incremento.

O ponto mais importante aqui é o tamanho do passo. TDD funciona melhor quando o incremento é pequeno o bastante para caber na sua cabeça sem esforço excessivo.

Por que TDD ajuda8.5.3

Sommerville destaca benefícios importantes do TDD:

  1. melhora a clareza sobre o que o código deveria fazer;
  2. aumenta a cobertura de código, porque cada incremento nasce junto com teste;
  3. torna o teste de regressão muito mais barato;
  4. simplifica a depuração, porque o defeito tende a estar no último incremento;
  5. faz os testes servirem também como documentação executável.

Mas também há um cuidado importante. TDD não substitui testes de sistema, desempenho, integração ou aceitação. Ele ajuda muito na construção incremental, mas não resolve sozinho a validação do software inteiro.

Exemplo 3

Vamos fazer TDD para uma regra simples de checkout.

Requisito pequeno: se o pedido tiver valor igual ou maior que R$ 200,00, o frete deve ser zero.

Passo 1. Vermelho8.0.1

Primeiro escrevemos o teste antes da implementação.

Arquivo tests/test_checkout.py:

from checkout import calcular_frete


def test_pedido_com_200_reais_ou_mais_tem_frete_gratis():
    assert calcular_frete(200.0) == 0.0

Se executarmos agora:

pytest -q

O resultado esperado é falha. Talvez o módulo nem exista ainda. Isso é o vermelho.

Passo 2. Verde8.0.2

Agora criamos o mínimo para fazer esse teste passar.

Arquivo checkout.py:

def calcular_frete(total_pedido: float) -> float:
    return 0.0

Esse código é claramente incompleto, mas faz o primeiro teste passar. Isso é importante no TDD. Você ainda não está tentando resolver tudo. Está resolvendo apenas o necessário para o comportamento atual.

Passo 3. Novo vermelho8.0.3

Agora escrevemos mais um teste.

from checkout import calcular_frete


def test_pedido_com_200_reais_ou_mais_tem_frete_gratis():
    assert calcular_frete(200.0) == 0.0


def test_pedido_abaixo_de_200_reais_paga_frete_padrao():
    assert calcular_frete(150.0) == 15.0

Ao rodar de novo, o segundo teste falha. Voltamos ao vermelho.

Passo 4. Novo verde8.0.4

Implementamos o mínimo para atender os dois testes.

def calcular_frete(total_pedido: float) -> float:
    if total_pedido >= 200.0:
        return 0.0
    return 15.0

Agora os dois passam. Estamos no verde novamente.

Passo 5. Azul8.0.5

Neste ponto, podemos melhorar o código sem mudar o comportamento. Suponha que você queira explicitar o valor do frete padrão.

FRETE_PADRAO = 15.0
LIMITE_FRETE_GRATIS = 200.0


def calcular_frete(total_pedido: float) -> float:
    if total_pedido >= LIMITE_FRETE_GRATIS:
        return 0.0
    return FRETE_PADRAO

Depois da refatoração, você roda a suíte novamente. Se tudo continuar passando, o azul foi seguro.

Passo 6. Continuar o ciclo8.0.6

O próximo passo natural seria testar entrada inválida:

import pytest

from checkout import calcular_frete


def test_total_negativo_dispara_erro():
    with pytest.raises(ValueError):
        calcular_frete(-10.0)

Falhou? Vermelho.

Implemente:

FRETE_PADRAO = 15.0
LIMITE_FRETE_GRATIS = 200.0


def calcular_frete(total_pedido: float) -> float:
    if total_pedido < 0:
        raise ValueError("total invalido")

    if total_pedido >= LIMITE_FRETE_GRATIS:
        return 0.0

    return FRETE_PADRAO

Passou? Verde.

Se quiser reorganizar nomes ou estrutura, azul.

O que esse ciclo ensina8.5.4

Repare na diferença de mentalidade. Em vez de tentar imaginar a implementação inteira de uma vez, você caminha em passos pequenos e verificáveis. Isso diminui ambiguidade e reduz o risco de escrever muito código sem saber se ele atende ao requisito.


No TDD, a falha inicial do teste é desejada. Ela mostra que o teste realmente está exercitando algo novo. Se o teste novo já nasce verde sem motivo claro, ou ele está mal escrito ou não está verificando nada útil.

Quando TDD ajuda menos8.5.5

Sommerville também chama atenção para limites do TDD. Ele tende a ser mais útil quando:

  1. você está construindo software novo;
  2. a funcionalidade cabe em incrementos pequenos;
  3. há automação de testes rápida;
  4. a unidade pode ser isolada com facilidade.

Ele tende a ser menos direto quando:

  1. há forte dependência de sistemas legados grandes;
  2. há concorrência complexa com múltiplas threads;
  3. a funcionalidade depende fortemente de interação visual complexa;
  4. o problema ainda está mal entendido a ponto de você nem conseguir escrever bons testes.

Testes de release e o caminho até a aceitação8.6

Depois dos testes de desenvolvimento, Sommerville separa o teste de release como um processo voltado a verificar se uma versão está boa o suficiente para sair da equipe de desenvolvimento.

Aqui há duas diferenças importantes em relação aos testes de desenvolvimento:

  1. idealmente, outra equipe deve ser responsável por esse teste;
  2. o foco sai da descoberta local de bugs e vai para a validação da versão diante dos requisitos.

É por isso que o teste de release é frequentemente descrito como um teste mais caixa-preta. O testador não está olhando tanto para a implementação interna. Ele está olhando para entradas, saídas, requisitos, cenários e comportamento esperado da versão.

Testes baseados em requisitos8.6.1

Um dos pontos mais úteis do capítulo 8.3 é a ideia de testes baseados em requisitos. A lógica é simples e poderosa:

  1. você pega um requisito;
  2. deriva um ou mais testes a partir dele;
  3. mantém rastreabilidade entre requisito e testes.

Isso é importante porque um requisito raramente é coberto por um único teste. Em geral, você precisa de vários casos para demonstrar cobertura razoável.

Testes de cenário8.6.2

Sommerville também destaca os testes de cenário, que partem de uma história realista de uso. Eles são muito úteis porque aproximam o teste da prática do usuário.

Em vez de testar apenas uma função isolada, você descreve algo como:

  1. cliente monta carrinho;
  2. totaliza R$ 230,00;
  3. informa CEP válido;
  4. confirma compra;
  5. sistema aplica frete grátis;
  6. resumo final mostra total correto.

Esse tipo de cenário serve como ponte entre requisitos, release e aceitação.

Teste de aceitação: o sistema está bom o suficiente para uso?8.7

O teste de aceitação, para Sommerville, é parte dos testes de usuário. Ele acontece quando o cliente avalia se o sistema deve ou não ser aceito para uso operacional.

Esse ponto é essencial. Enquanto o teste unitário pergunta se uma pequena parte funciona, o teste de aceitação pergunta algo muito mais próximo do negócio:

"isso está bom o suficiente para ser usado no ambiente real?"

Essa pergunta envolve funcionalidade, mas também pode envolver desempenho, usabilidade, fluxo operacional e adequação ao trabalho real.

Os seis estágios do teste de aceitação8.7.1

Sommerville descreve seis estágios para o processo de aceitação:

  1. definir critérios de aceitação;
  2. planejar os testes de aceitação;
  3. derivar os testes de aceitação;
  4. executar os testes de aceitação;
  5. negociar os resultados;
  6. aceitar ou rejeitar o sistema.

Isso mostra por que aceitação não é apenas "rodar uns testes no final". É um processo com critério, planejamento e decisão.

Um exemplo simples de critério de aceitação8.7.2

Suponha o seguinte requisito de negócio para um checkout:

"Pedidos com total igual ou superior a R$ 200,00 devem receber frete grátis no resumo final da compra."

Um possível critério de aceitação poderia ser:

"Quando o cliente finalizar um pedido com total de R$ 200,00 ou mais, o sistema deve exibir frete igual a R$ 0,00 e total final sem cobrança adicional de frete."

Perceba a diferença:

  1. no teste unitário, você pode testar apenas a função calcular_frete;
  2. na aceitação, você quer confirmar que o comportamento aparece corretamente para o uso final.
Exemplo 4

Vamos transformar o requisito acima em um cenário de aceitação simples.

Cenário

  1. O cliente adiciona produtos ao carrinho.
  2. O subtotal chega a R$ 230,00.
  3. O cliente avança para o checkout.
  4. O sistema calcula o frete.
  5. O resumo final deve mostrar frete R$ 0,00.
  6. O total final deve permanecer R$ 230,00.

Uma implementação simplificada para demonstração poderia ser esta:

def resumo_do_pedido(subtotal: float) -> dict:
    if subtotal < 0:
        raise ValueError("subtotal invalido")

    frete = 0.0 if subtotal >= 200.0 else 15.0
    total = subtotal + frete

    return {
        "subtotal": subtotal,
        "frete": frete,
        "total": total,
    }

E um teste automatizado de aceitação simplificado poderia ficar assim:

from checkout import resumo_do_pedido


def test_aceitacao_frete_gratis_para_pedido_acima_do_limite():
    resumo = resumo_do_pedido(230.0)

    assert resumo["subtotal"] == 230.0
    assert resumo["frete"] == 0.0
    assert resumo["total"] == 230.0

Este teste está automatizado, mas a ideia central dele não é mais apenas verificar uma unidade interna. Ele está verificando uma regra de negócio mais próxima do uso observado pelo cliente.

Em um sistema real, a aceitação pode envolver interface, dados reais, usuários reais, ambiente operacional e discussão formal com o cliente. O exemplo acima é apenas uma versão didática e simplificada.

Aceitação não é sempre tudo ou nada8.7.3

Um ponto interessante do Sommerville é que, na prática, aceitação nem sempre funciona como um corte totalmente rígido. Muitas vezes, o cliente quer começar a usar o sistema logo porque já treinou a equipe, já alterou processo interno ou já comprou infraestrutura.

Nesses casos, pode acontecer uma aceitação condicional:

  1. o cliente aceita começar a implantação;
  2. o fornecedor se compromete a corrigir problemas urgentes rapidamente;
  3. a decisão é negociada com base em risco e impacto.

Isso ajuda a entender que engenharia de software não é apenas técnica. Há também contexto organizacional, custo, prazo e decisão de negócio.


Um software pode passar em muitos testes técnicos e ainda assim falhar em aceitação se não atender ao uso real, ao fluxo de trabalho ou ao nível de qualidade esperado pelo cliente.

O que cada tipo de teste resolve8.8

Vamos fechar a lógica da aula com uma comparação direta.

Tipo de teste Foco principal Exemplo de pergunta
Unitário Regra pequena isolada calcular_frete(200) retorna 0.0?
Integração / componente Comunicação entre partes O serviço grava o pedido corretamente no repositório?
Sistema Comportamento do sistema inteiro O checkout completo funciona do carrinho ao pagamento?
Release Validação da versão para sair da equipe Esta versão atende aos requisitos definidos?
Aceitação Prontidão para uso pelo cliente O cliente considera essa versão adequada para operar?

Se você guardar essa tabela, já evita uma série de confusões comuns.

A ideia central que organiza tudo8.9

O grande aprendizado deste tópico é que testar não é uma atividade única, mas um conjunto de verificações em níveis diferentes.

Você usa testes unitários para ganhar rapidez e precisão sobre regras pequenas. Usa TDD para construir essas regras de forma incremental e verificável. Usa testes de aceitação para responder a pergunta que realmente fecha a entrega: se o sistema está bom o suficiente para o cliente usar.


Teste unitário ajuda você a construir corretamente partes pequenas. TDD ajuda você a construir essas partes em passos seguros. Teste de aceitação ajuda a verificar se o sistema, como solução, realmente atende ao uso esperado.

Questões8.10

1. Explique a diferença entre teste unitário, teste de integração, teste de sistema e teste de aceitação. Qual pergunta principal cada um deles tenta responder?

2. Por que passar em testes unitários não é suficiente para concluir que o sistema inteiro está pronto para entrega?

3. Descreva o ciclo do TDD usando as cores vermelho, verde e azul. O que acontece em cada etapa?

4. Em um processo de TDD, por que a falha inicial de um teste novo não deve ser interpretada como problema do processo?

5. Considere a regra a seguir: pedidos com subtotal maior ou igual a R$ 200,00 recebem frete grátis. Proponha pelo menos quatro casos de teste unitário relevantes para essa regra.

6. Qual é a diferença entre um teste baseado em requisito e um teste de cenário? Por que ambos são úteis em testes de release?

7. Explique por que TDD ajuda a reduzir o custo do teste de regressão.

8. Em que situações o uso de mocks ou objetos falsos pode ser útil em testes unitários?

9. Segundo Sommerville, quais são os estágios do teste de aceitação? Explique resumidamente a função de cada um.

10. O que significa uma aceitação condicional de sistema? Por que ela pode acontecer mesmo quando ainda existem problemas conhecidos?

Gabarito

1. Teste unitário verifica unidades pequenas isoladas. Integração verifica comunicação entre partes. Sistema verifica o comportamento do sistema completo. Aceitação verifica se o cliente considera o sistema adequado para uso real.

2. Porque testes unitários não cobrem necessariamente integração entre módulos, comportamento do sistema completo, ambiente real de uso, requisitos não funcionais nem critérios de aceitação do cliente.

3. Vermelho significa escrever um teste novo e vê-lo falhar. Verde significa implementar o mínimo para fazê-lo passar. Azul significa refatorar e melhorar o código sem mudar o comportamento, mantendo todos os testes passando.

4. Porque no TDD o teste novo é escrito antes da funcionalidade existir. A falha inicial mostra justamente que o teste está exercitando um comportamento ainda não implementado.

5. Exemplos: subtotal 150.0 deve cobrar frete padrão; subtotal 199.99 deve cobrar frete padrão; subtotal 200.0 deve dar frete grátis; subtotal 350.0 deve dar frete grátis; subtotal negativo deve disparar erro.

6. Teste baseado em requisito deriva casos a partir de requisitos explícitos. Teste de cenário deriva casos a partir de histórias realistas de uso. O primeiro ajuda na cobertura formal dos requisitos. O segundo aproxima o teste do uso prático do sistema.

7. Porque o conjunto de testes automatizados cresce junto com o código. Sempre que o sistema muda, os testes antigos podem ser reexecutados rapidamente para verificar se a mudança não introduziu regressões.

8. Quando a unidade depende de banco de dados, serviço remoto, relógio do sistema, fila, API externa ou qualquer dependência que torne o teste lento, instável ou difícil de controlar. O mock ajuda a isolar a unidade testada.

9. Definir critérios de aceitação, planejar testes, derivar testes, executar testes, negociar resultados e aceitar ou rejeitar o sistema. Em conjunto, esses estágios estruturam a decisão formal sobre a prontidão do sistema para uso.

10. Significa que o cliente aceita iniciar uso ou implantação mesmo com problemas conhecidos, desde que haja acordo sobre correções e riscos. Isso pode ocorrer porque o custo de não usar o sistema naquele momento é maior que o custo de corrigir os problemas remanescentes.

Próximos passos8.11

Na próxima aula, avançaremos para Evolução de Software. Depois de entender como validar e testar um sistema, o próximo passo é estudar por que o software continua mudando mesmo depois da entrega, como essas mudanças afetam custo e manutenção e por que evoluir bem é parte central da engenharia de software profissional.