
Passo 1 de 7
Do contrato ao teste do estado inicial
Crie a classe Playlist localmente e escreva um primeiro teste que observe seu estado inicial apenas pela interface pública.
Trilha de aprendizado · Nível 8 · Tutorial 3
Escrever testes de classes que verificam inicialização, transições de estado, rejeição de operações inválidas e independência entre instâncias.
Do contrato ao teste do estado inicial
Crie a classe Playlist localmente e escreva um primeiro teste que observe seu estado inicial apenas pela interface pública. 2 min
Verificar retorno e efeito de uma operação
Aprenda a testar separadamente o valor devolvido por um método e a mudança observável no estado do objeto. 2 min
Acompanhar sequências e verificar invariantes
Teste uma sequência completa de transições da Playlist, conferindo estados intermediários, invariantes e resultados concretos. 2 min
Confirmar que uma rejeição preserva o estado
Verifique tanto a exceção esperada quanto a preservação do estado público após uma operação inválida. 3 min
Manter cada teste independente
Organize cada função de teste para criar e preparar o próprio objeto, permitindo executá-la sozinha ou em qualquer ordem. 2 min
Detectar vazamento entre instâncias
Construa um teste com duas playlists para verificar, pela interface pública, que alterações em uma instância não vazam para a outra. 2 min
Aplicação final: reunir e executar a suíte
Consolide a implementação e os testes da Playlist, execute a suíte localmente e revise como cada cenário verifica o contrato público. 4 min

Passo 1 de 7
Crie a classe Playlist localmente e escreva um primeiro teste que observe seu estado inicial apenas pela interface pública.
Usaremos uma Playlist em memória: ela possui capacidade positiva, não aceita títulos repetidos e preserva a ordem de inclusão. Não há arquivos de áudio nem serviços externos.
Neste primeiro cenário, o foco é somente a criação válida. Ao construir Playlist(3), o teste deve observar que:
capacidade vale 3;listar_faixas() devolve uma lista vazia.A lista interna _faixas é detalhe de implementação. O teste deve consultar o estado pela interface pública.
O teste se conecta apenas aos pontos públicos do objeto. O armazenamento interno permanece encapsulado.

Verifique o comportamento visível; não acople o teste ao atributo interno _faixas.
No seu computador, crie uma pasta de projeto com esta estrutura:
playlist_project/
playlist.pytests/test_playlist.pySalve o código abaixo em playlist.py. A implementação já contém as operações usadas ao longo do tutorial, mas agora examinaremos apenas o estado inicial.
Implementação completa da classe usada no tutorial.
class Playlist:
def __init__(self, capacidade):
if capacidade <= 0:
raise ValueError("A capacidade deve ser positiva")
self.capacidade = capacidade
self._faixas = []
def listar_faixas(self):
return list(self._faixas)
def adicionar(self, titulo):
if titulo in self._faixas:
raise ValueError("A faixa já está na playlist")
if len(self._faixas) >= self.capacidade:
raise ValueError("A playlist está cheia")
self._faixas.append(titulo)
return len(self._faixas)
def remover(self, titulo):
if titulo not in self._faixas:
raise ValueError("A faixa não está na playlist")
self._faixas.remove(titulo)Salve o teste abaixo em tests/test_playlist.py. Cada função test_* pode criar diretamente a instância necessária; não é preciso organizar os testes em classes.
A expectativa [] vem do contrato de uma Playlist recém-criada, não de uma inspeção de _faixas.
Teste do estado inicial pela interface pública.
from playlist import Playlist
def test_playlist_inicia_com_capacidade_e_sem_faixas():
playlist = Playlist(3)
assert playlist.capacidade == 3
assert playlist.listar_faixas() == []No terminal, com o ambiente que contém o pytest ativado, execute:
python -m pytestConsidere o contrato público apresentado. Qual alternativa produz o teste menos acoplado ao armazenamento interno?

Passo 2 de 7
Aprenda a testar separadamente o valor devolvido por um método e a mudança observável no estado do objeto.
Uma chamada de método pode produzir dois resultados distintos:
O contrato determina o que deve ser verificado. Em adicionar(), testaremos o novo total retornado e a lista atualizada. Em remover(), o retorno esperado é None, mas a lista ainda deve mudar.

A mesma chamada pode devolver um valor e alterar o estado público do objeto. Cada resultado precisa de sua própria verificação.
Pelo contrato, adicionar() inclui uma faixa válida e devolve o total de faixas após a inclusão. A chamada fica na ação do teste; depois, verificamos separadamente o retorno e o estado público.
Acrescente este teste ao arquivo de testes usado no step anterior.
from playlist import Playlist
def test_adicionar_retorna_total_e_inclui_faixa():
playlist = Playlist(capacidade=3)
total = playlist.adicionar("Azul")
assert total == 1
assert playlist.listar_faixas() == ["Azul"]
assert playlist.capacidade == 3Dica
Não use o próprio objeto para calcular o resultado esperado. Escreva explicitamente 1 e ["Azul"]: assim, uma implementação defeituosa não define a expectativa do teste.
Para testar remover(), primeiro prepare uma playlist que contenha a faixa. Faça isso pela operação pública adicionar(), sem alterar _faixas diretamente.
Pelo contrato, remover() exclui a faixa e retorna None. Portanto, um teste que verificasse apenas o retorno não confirmaria a remoção.
def test_remover_retorna_none_e_retira_faixa():
playlist = Playlist(capacidade=3)
playlist.adicionar("Azul")
retorno = playlist.remover("Azul")
assert retorno is None
assert playlist.listar_faixas() == []
assert playlist.capacidade == 3Exemplo
assert retorno is None expressa diretamente que o contrato exige o objeto único None. Já assert playlist.listar_faixas() == [] verifica o efeito observável da remoção.
Uma playlist vazia recebe retorno = playlist.adicionar("Sol"). Complete: assert retorno == ___
Após preparar uma playlist com "Sol", executamos retorno = playlist.remover("Sol"). Complete: assert retorno is ___
Depois de remover a única faixa, complete a verificação pública: assert playlist.listar_faixas() == ___

Passo 3 de 7
Teste uma sequência completa de transições da Playlist, conferindo estados intermediários, invariantes e resultados concretos.
Considere uma Playlist com capacidade 2. O cenário inclui duas faixas, atinge o limite, remove uma delas e ocupa novamente a vaga.
Todas essas operações pertencem ao mesmo teste porque descrevem um único fluxo de comportamento. Depois de cada transição relevante, verifique tanto o conteúdo esperado quanto as invariantes:
Cada quadro representa o estado observável logo depois de uma operação pública.

A ordem esperada é: [Intro] → [Intro, Tema] → [Tema] → [Tema, Final].
A lista esperada verifica quais faixas estão presentes e em qual ordem. As demais asserções verificam regras gerais da Playlist. Uma categoria não substitui a outra: uma lista pode ter quantidade válida e, ainda assim, conter títulos errados ou fora de ordem.
Adicione este teste ao arquivo de testes da Playlist. Ele usa somente a interface pública.
from playlist import Playlist
def test_sequencia_ocupa_libera_e_reocupa_capacidade():
playlist = Playlist(2)
assert playlist.adicionar("Intro") == 1
faixas = playlist.listar_faixas()
assert faixas == ["Intro"]
assert 0 <= len(faixas) <= playlist.capacidade
assert len(faixas) == len(set(faixas))
assert playlist.capacidade == 2
assert playlist.adicionar("Tema") == 2
faixas = playlist.listar_faixas()
assert faixas == ["Intro", "Tema"]
assert 0 <= len(faixas) <= playlist.capacidade
assert len(faixas) == len(set(faixas))
assert playlist.capacidade == 2
assert playlist.remover("Intro") is None
faixas = playlist.listar_faixas()
assert faixas == ["Tema"]
assert 0 <= len(faixas) <= playlist.capacidade
assert len(faixas) == len(set(faixas))
assert playlist.capacidade == 2
assert playlist.adicionar("Final") == 2
faixas = playlist.listar_faixas()
assert faixas == ["Tema", "Final"]
assert 0 <= len(faixas) <= playlist.capacidade
assert len(faixas) == len(set(faixas))
assert playlist.capacidade == 2Dica
Se o teste verificasse somente ["Tema", "Final"] no fim, uma transição intermediária incorreta poderia passar despercebida ou ser mascarada por uma operação posterior. As verificações intermediárias localizam a primeira transição que rompeu o contrato.
Exemplo
Depois da segunda inclusão, len(faixas) == 2 seria verdadeiro tanto para ["Intro", "Tema"] quanto para ["Tema", "Intro"]. A invariante da quantidade é respeitada nos dois casos, mas somente a comparação com a lista esperada confirma conteúdo e ordem.
Relacione cada momento ou expressão à garantia oferecida pelo teste.
Toque em um item e depois no par correspondente.

Passo 4 de 7
Verifique tanto a exceção esperada quanto a preservação do estado público após uma operação inválida.
Na Playlist, adicionar além da capacidade, adicionar um título repetido ou remover uma faixa ausente deve gerar ValueError sem alterar o estado.
Por isso, o teste precisa verificar duas partes distintas do contrato:
Use um estado inicial não vazio. Assim, uma perda, inclusão parcial ou reordenação fica visível.
O retrato do estado anterior serve como referência para avaliar o objeto depois da chamada rejeitada.

A tentativa falha, mas o estado observável antes e depois permanece igual.
Atenção
Uma exceção não garante, por si só, que nada foi modificado. A ausência de mutação precisa fazer parte do contrato da operação. Quando fizer parte, o teste deve verificá-la explicitamente.
Antes da tentativa inválida, registre separadamente os valores observáveis. listar_faixas() devolve uma cópia, então a lista guardada funciona como um retrato do conteúdo e da ordem naquele momento. Não é necessário acessar _faixas nem guardar outra referência para a própria Playlist.
Acrescente este teste ao arquivo de testes da Playlist:
import pytest
from playlist import Playlist
def test_adicionar_faixa_duplicada_preserva_estado():
playlist = Playlist(capacidade=3)
playlist.adicionar("Horizonte")
playlist.adicionar("Chuva")
capacidade_antes = playlist.capacidade
faixas_antes = playlist.listar_faixas()
with pytest.raises(ValueError):
playlist.adicionar("Horizonte")
assert playlist.capacidade == capacidade_antes
assert playlist.listar_faixas() == faixas_antesDica
Fazer antes = playlist não cria um retrato: os dois nomes apontariam para o mesmo objeto mutável. Guardar a capacidade e a cópia retornada por listar_faixas() permite comparar valores anteriores independentes.
Para lotação e remoção ausente, a estrutura continua sendo: preparar um estado observável, registrar o retrato, executar somente a chamada rejeitada dentro de pytest.raises e comparar o estado posterior.
Os dois testes também preservam conteúdo, ordem e capacidade:
def test_adicionar_alem_da_capacidade_preserva_estado():
playlist = Playlist(capacidade=2)
playlist.adicionar("Horizonte")
playlist.adicionar("Chuva")
capacidade_antes = playlist.capacidade
faixas_antes = playlist.listar_faixas()
with pytest.raises(ValueError):
playlist.adicionar("Vento")
assert playlist.capacidade == capacidade_antes
assert playlist.listar_faixas() == faixas_antes
def test_remover_faixa_ausente_preserva_estado():
playlist = Playlist(capacidade=3)
playlist.adicionar("Horizonte")
playlist.adicionar("Chuva")
capacidade_antes = playlist.capacidade
faixas_antes = playlist.listar_faixas()
with pytest.raises(ValueError):
playlist.remover("Vento")
assert playlist.capacidade == capacidade_antes
assert playlist.listar_faixas() == faixas_antesUma playlist contém Horizonte e Chuva. Uma tentativa de adicionar Horizonte novamente gera ValueError. Quais valores você registraria antes da tentativa, o que verificaria depois e por que testar somente a exceção seria insuficiente?
Escreva pelo menos 80 caracteres (0/80).

Passo 5 de 7
Organize cada função de teste para criar e preparar o próprio objeto, permitindo executá-la sozinha ou em qualquer ordem.
Cada função test_* deve criar uma nova Playlist e construir, por métodos públicos, o estado de que precisa. Um objeto mutável global pode carregar alterações de um teste para outro, fazendo o resultado depender da ordem de execução.
A ordem das operações dentro de um teste pode ser necessária ao cenário. O problema aparece quando uma função depende de outra ter sido executada antes.
Compare uma suíte encadeada com testes que possuem preparações próprias.

À esquerda, testes compartilham um estado que muda ao longo da suíte. À direita, cada teste começa com um objeto novo e controla todo o seu cenário.
Neste exemplo inadequado, o segundo teste só funciona se o primeiro deixar a faixa na playlist.
from playlist import Playlist
playlist = Playlist(2)
def test_adicionar_faixa():
playlist.adicionar("Horizonte")
assert playlist.listar_faixas() == ["Horizonte"]
def test_remover_faixa():
# Depende da alteração feita por test_adicionar_faixa.
playlist.remover("Horizonte")
assert playlist.listar_faixas() == []Agora cada função cria sua instância. O teste de remoção prepara a faixa necessária antes de executar a ação principal.
from playlist import Playlist
def test_adicionar_faixa():
playlist = Playlist(2)
total = playlist.adicionar("Horizonte")
assert total == 1
assert playlist.listar_faixas() == ["Horizonte"]
def test_remover_faixa():
playlist = Playlist(2)
playlist.adicionar("Horizonte")
retorno = playlist.remover("Horizonte")
assert retorno is None
assert playlist.listar_faixas() == []Dica
No terminal, a partir da pasta do projeto, execute cada função sozinha e depois inverta a ordem dos identificadores:
python -m pytest tests/test_playlist.py::test_adicionar_faixa
python -m pytest tests/test_playlist.py::test_remover_faixa
python -m pytest tests/test_playlist.py::test_remover_faixa tests/test_playlist.py::test_adicionar_faixa
Essas execuções podem revelar dependências, mas passar em algumas ordens não prova independência. A garantia vem de cada teste preparar integralmente o próprio cenário.
Em uma única função de teste, adicionar uma faixa e depois removê-la é uma dependência indevida, mesmo quando a playlist foi criada dentro da própria função.
Se a suíte passa quando os testes são executados em uma ordem escolhida, isso prova que eles são independentes.

Passo 6 de 7
Construa um teste com duas playlists para verificar, pela interface pública, que alterações em uma instância não vazam para a outra.
Criar um objeto novo em cada função de teste evita dependências entre testes. Porém, isso não garante que duas instâncias da mesma classe mantenham estados separados.
Para verificar essa outra propriedade, crie as duas playlists antes de qualquer alteração. Depois, modifique uma por vez e observe ambas por listar_faixas(). Se uma faixa adicionada a uma playlist aparecer na outra, existe vazamento de estado mutável.
Cada operação deve afetar somente a instância que a recebeu. As duas playlists podem existir ao mesmo tempo sem compartilhar suas coleções de faixas.

A independência é observada pelo conteúdo público de cada playlist após cada alteração.
Adicione este teste ao arquivo de testes já usado no projeto. Ele verifica primeiro se a alteração da primeira não alcança a segunda; depois faz a operação inversa. As expectativas usam somente a interface pública.
Execute o teste com python -m pytest no seu projeto local.
from playlist import Playlist
def test_playlists_mantem_faixas_independentes():
primeira = Playlist(capacidade=2)
segunda = Playlist(capacidade=2)
primeira.adicionar("Horizonte")
assert primeira.listar_faixas() == ["Horizonte"]
assert segunda.listar_faixas() == []
segunda.adicionar("Travessia")
assert primeira.listar_faixas() == ["Horizonte"]
assert segunda.listar_faixas() == ["Travessia"]
Dica
Se a segunda playlist já contiver "Horizonte" após a primeira inclusão, ou se a primeira passar a conter "Travessia" após a segunda, uma operação afetou a instância errada. O teste não precisa comparar atributos internos nem a identidade de listas: o comportamento público já revela o compartilhamento indevido.
Considere duas playlists inicialmente vazias. Escreva as verificações que você faria após adicionar "Norte" somente à primeira e, depois, "Sul" somente à segunda. Explique qual divergência indicaria que as instâncias compartilham estado indevidamente.
Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 7
Consolide a implementação e os testes da Playlist, execute a suíte localmente e revise como cada cenário verifica o contrato público.
No seu computador, reúna a implementação e os testes nesta estrutura:
playlist_projeto/
├── playlist.py
└── tests/
└── test_playlist.pyOs testes observarão somente capacidade, os retornos dos métodos e listar_faixas(). O atributo interno _faixas não será consultado diretamente.
Crie ou substitua o arquivo playlist.py por esta implementação completa:
class Playlist:
def __init__(self, capacidade):
if capacidade <= 0:
raise ValueError("A capacidade deve ser positiva")
self.capacidade = capacidade
self._faixas = []
def adicionar(self, titulo):
if titulo in self._faixas:
raise ValueError("A faixa já está na playlist")
if len(self._faixas) >= self.capacidade:
raise ValueError("A playlist está cheia")
self._faixas.append(titulo)
return len(self._faixas)
def remover(self, titulo):
if titulo not in self._faixas:
raise ValueError("A faixa não está na playlist")
self._faixas.remove(titulo)
def listar_faixas(self):
return self._faixas.copy()Salve o código abaixo em tests/test_playlist.py. Cada função cria as próprias instâncias e prepara seu cenário por operações públicas. A ordem interna de um cenário importa, mas nenhuma função depende de outra função de teste.
A suíte cobre inicialização, retorno e efeito, sequência com invariantes, rejeições sem mutação e independência entre instâncias.
import pytest
from playlist import Playlist
def test_inicializa_vazia_com_capacidade_configurada():
playlist = Playlist(3)
assert playlist.capacidade == 3
assert playlist.listar_faixas() == []
def test_adicionar_devolve_total_e_preserva_ordem():
playlist = Playlist(3)
total = playlist.adicionar("Aurora")
assert total == 1
assert playlist.listar_faixas() == ["Aurora"]
def test_remover_devolve_none_e_altera_conteudo():
playlist = Playlist(3)
playlist.adicionar("Aurora")
playlist.adicionar("Horizonte")
retorno = playlist.remover("Aurora")
assert retorno is None
assert playlist.listar_faixas() == ["Horizonte"]
def test_sequencia_preserva_ordem_capacidade_e_unicidade():
playlist = Playlist(2)
assert playlist.adicionar("Aurora") == 1
assert playlist.listar_faixas() == ["Aurora"]
assert playlist.adicionar("Horizonte") == 2
assert playlist.listar_faixas() == ["Aurora", "Horizonte"]
assert len(playlist.listar_faixas()) <= playlist.capacidade
assert len(playlist.listar_faixas()) == len(set(playlist.listar_faixas()))
assert playlist.remover("Aurora") is None
assert playlist.listar_faixas() == ["Horizonte"]
assert playlist.adicionar("Maré") == 2
assert playlist.listar_faixas() == ["Horizonte", "Maré"]
assert playlist.capacidade == 2
assert len(playlist.listar_faixas()) <= playlist.capacidade
assert len(playlist.listar_faixas()) == len(set(playlist.listar_faixas()))
def test_duplicata_e_rejeitada_sem_alterar_estado():
playlist = Playlist(3)
playlist.adicionar("Aurora")
antes_capacidade = playlist.capacidade
antes_faixas = playlist.listar_faixas()
with pytest.raises(ValueError):
playlist.adicionar("Aurora")
assert playlist.capacidade == antes_capacidade
assert playlist.listar_faixas() == antes_faixas
def test_excesso_de_capacidade_e_rejeitado_sem_alterar_estado():
playlist = Playlist(2)
playlist.adicionar("Aurora")
playlist.adicionar("Horizonte")
antes_capacidade = playlist.capacidade
antes_faixas = playlist.listar_faixas()
with pytest.raises(ValueError):
playlist.adicionar("Maré")
assert playlist.capacidade == antes_capacidade
assert playlist.listar_faixas() == antes_faixas
def test_remocao_ausente_e_rejeitada_sem_alterar_estado():
playlist = Playlist(3)
playlist.adicionar("Aurora")
playlist.adicionar("Horizonte")
antes_capacidade = playlist.capacidade
antes_faixas = playlist.listar_faixas()
with pytest.raises(ValueError):
playlist.remover("Maré")
assert playlist.capacidade == antes_capacidade
assert playlist.listar_faixas() == antes_faixas
def test_instancias_mantem_estados_independentes():
primeira = Playlist(3)
segunda = Playlist(2)
primeira.adicionar("Aurora")
assert primeira.listar_faixas() == ["Aurora"]
assert segunda.listar_faixas() == []
segunda.adicionar("Maré")
assert segunda.listar_faixas() == ["Maré"]
assert primeira.listar_faixas() == ["Aurora"]Abra o terminal na pasta playlist_projeto. Execute primeiro a suíte completa e depois dois cenários isolados:
python -m pytest
python -m pytest tests/test_playlist.py::test_sequencia_preserva_ordem_capacidade_e_unicidade
python -m pytest tests/test_playlist.py::test_instancias_mantem_estados_independentesDica
Na suíte completa, o resultado esperado é a aprovação dos oito testes. Nas seleções seguintes, somente o teste indicado deve ser executado e aprovado. Se houver falha, leia a comparação exibida pelo pytest e localize qual estado observado contrariou o contrato.
Escolha um dos testes da suíte e cole-o na resposta. Registre também o resultado observado ao executá-lo. Depois, explique como a suíte detectaria: (1) uma alteração parcial causada por uma operação rejeitada e (2) um vazamento de faixas entre instâncias.
Escreva pelo menos 120 caracteres (0/120).
Resumo
Use esta lista para revisar sua solução antes de encerrar:
Parabéns! Você concluiu: Testar mudanças de estado em objetos
100 XP
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks