Skip to content

Instantly share code, notes, and snippets.

@MM-coder
Created December 16, 2023 21:00
Show Gist options
  • Save MM-coder/ad200d82ea335f0f76b323ae75c3a79f to your computer and use it in GitHub Desktop.
Save MM-coder/ad200d82ea335f0f76b323ae75c3a79f to your computer and use it in GitHub Desktop.
Unit tests for the FeiraVirtual project
import os
import random
import re
from pathlib import Path
import pytest
import string
from classes import Utilizador, Avaliacao, Artigo, FeiraVirtual, Mercado
def _random_string(length: int) -> str:
"""Gera uma ‘string’ aletória de um dado comprimento"""
characters = string.ascii_letters + string.digits
return "".join(random.choice(characters) for _ in range(length))
class TestUtilizador:
@pytest.fixture
def utilizador(self):
"""Fixture que cria um utilizador para testar."""
artigo_exemplo = Artigo(
nome="Computador", preco=10.0, tipologia="Tecnologia", quantidade=1
)
avaliacao_exemplo = Avaliacao(estrelas=4, comentario="Exemplo")
return Utilizador(
nome="Mauro",
interesses=["Tecnologia"],
artigos_disponiveis=[artigo_exemplo],
avaliacoes=[avaliacao_exemplo],
)
def test_editar_conta(self, utilizador):
"""Testa editar interesses dos utilizadores"""
novos_interesses = ["Gaming", "Tecnologia"]
novos_artigos = [
Artigo(nome="Computador", preco=10.0, tipologia="Tecnologia", quantidade=1)
]
utilizador.editar_conta(novos_interesses, novos_artigos)
assert "Gaming" in utilizador.interesses
assert novos_artigos[0] in utilizador.artigos_disponiveis
# O interesse já existente deverá permanecer
assert "Tecnologia" in utilizador.interesses
def test_deixar_avaliacao(self, utilizador):
"""Testa dexiar avaliações válidas e inválidas."""
utilizador.deixar_avaliacao(estrelas=4, comentario="Bom")
last_avaliacao = utilizador.avaliacoes[-1]
assert last_avaliacao.estrelas == 4
assert last_avaliacao.comentario == "Bom"
with pytest.raises(ValueError):
utilizador.deixar_avaliacao(estrelas=6, comentario="Muito Bom")
def test_alterar_pycoins(self, utilizador):
"""Testa atualizar pycoins com valores válidos e inválidos."""
# Apenas chamar o random uma vez
integer: int = random.randint(0, 100000)
utilizador.alterar_pycoins(integer)
assert utilizador.pycoins == integer
utilizador.alterar_pycoins(-integer)
assert (
utilizador.pycoins == 0
), "O programa não deve aceitar valores de pycoins negativos"
class TestArtigo:
# Método para configurar um artigo de teste antes de cada método
@pytest.fixture
def artigo(self):
"""Fixture que cria um artigo para testar."""
return Artigo(
nome="Artigo Teste", preco=10.0, tipologia="Tipo Teste", quantidade=5
)
def test_editar_nome(self, artigo):
"""Testa a alteração do nome de um artigo."""
nome_aletorio = _random_string(random.randint(1, 100))
artigo.editar_nome(nome_aletorio)
assert artigo.nome == nome_aletorio
artigo.editar_nome("") # Testa com nome vazio
assert artigo.nome == ""
def test_ajustar_preco(self, artigo):
"""Testa a alteração do preço por uma percentagem."""
preco_atual = artigo.preco
percentagem_aletoria = random.randint(0, 100)
# Deverá reduzir o preço para metade
artigo.ajustar_preco(percentagem_aletoria)
assert artigo.preco == preco_atual * (percentagem_aletoria / 100)
with pytest.raises(ValueError):
# Testa com valor fora do intervalo
artigo.ajustar_preco(-percentagem_aletoria)
def test_editar_preco(self, artigo):
"""Testa a definição de um novo preço para o artigo."""
integer: int = random.randint(0, 100000)
artigo.editar_preco(integer)
assert artigo.preco == integer
artigo.editar_preco(-integer) # Testa com preço negativo
assert artigo.preco == 0
def test_editar_quantidade(self, artigo):
"""Testa a alteração da quantidade de um artigo."""
# Apenas chamar o random uma vez
integer: int = random.randint(0, 100000)
artigo.editar_quantidade(integer)
assert artigo.quantidade == integer
artigo.editar_quantidade(-integer) # Testa com quantidade negativa
assert artigo.quantidade == 0
def test_editar_tipo(self, artigo):
"""Testa a alteração da tipologia de um artigo."""
tipo_aletorio = _random_string(random.randint(1, 100))
artigo.editar_tipo(tipo_aletorio)
assert artigo.tipologia == tipo_aletorio
artigo.editar_tipo("") # Teste com tipo vazio
assert artigo.tipologia == ""
class TestFeiraVirtual:
@pytest.fixture
def feira(self):
"""Cria uma instância de FeiraVirtual para uso nos testes."""
feira = FeiraVirtual(utilizadores=[], mercado=Mercado())
return feira
def test_registar_utilizador(self, feira):
"""Testa o registro de um novo utilizador."""
nome_aletorio = _random_string(random.randint(1, 100))
feira.registar_utilizador(
nome=nome_aletorio, interesses=["Tecnologia", "Livros"]
)
assert len(feira.utilizadores) == 1
assert feira.utilizadores[0].nome == nome_aletorio
# Testa registar um novo utilizador com um nome já existente
with pytest.raises(ValueError):
feira.registar_utilizador(nome=nome_aletorio, interesses=["Cinema"])
def test_eliminar_conta(self, feira):
"""Testa a eliminação de uma conta."""
nome_aletorio = _random_string(random.randint(1, 100))
feira.registar_utilizador(nome=nome_aletorio, interesses=["Moda"])
assert feira.eliminar_conta(nome_utilizador=nome_aletorio) == 0
assert feira.eliminar_conta(nome_utilizador=nome_aletorio) == 1
def test_calcular_reputacao(self, feira):
"""Testa o cálculo da reputação de um utilizador."""
nome_aletorio = _random_string(random.randint(1, 100))
feira.registar_utilizador(
nome=nome_aletorio, interesses=["Arte"], artigos_disponiveis=[]
)
assert (
feira.calcular_reputacao(nome_utilizador=nome_aletorio) == 0
) # Sem avaliações
# Adicionar avaliações manualmente
utilizador = feira.utilizadores[0]
utilizador.avaliacoes = [
Avaliacao(estrelas=4, comentario="Bom"),
Avaliacao(estrelas=5, comentario="Excelente"),
]
assert feira.calcular_reputacao(nome_utilizador=nome_aletorio) == 4.5
assert (
feira.calcular_reputacao(
nome_utilizador=_random_string(random.randint(1, 100))
)
== -1
) # Utilizador
# inexistente
def test_importar_utilizadores_exemplo(self, feira, tmp_path: Path):
"""Testa a importação de utilizadores com o exemplo dado"""
exemplo = (
"nome;[interesse_1,interesse_2,…];[nome_artigo_1,preço,tipologia,quantidade&nome_artigo_2,…]\nAna;["
"tecnologia,moda];[telemóvel,5,tecnologia,1&raquete de ténis,10,desporto,2]\nManuel;[arte,"
"viagens];[quadro Van Gogh,50,arte,1&guitarra,10,música,1&PS5,25,jogos,4]\nSilvia;["
"livros];\nSofia;[jogos,música];[panela elétrica,8,cozinha,1&bilhete de avião para as Caraíbas,39,"
"viagens,2]\nAntónio;[viagens,cinema];[bola de futebol,16,desporto,1]\nPedro;[cinema,culinária,"
"tecnologia];[bicicleta,30,desporto,2]\nRaquel;[tecnologia,desporto,arte,livros];[escultura em "
"madeira,40,arte,1]\nPaulo;[livros];[Monopoly,10,jogos,1&coluna,19,musica,1&frigideira,9,"
"culinária,5]\nJoaquim;[jogos,música,cinema];[vinil best of k-pop,8,musica,2]\nCristina;[moda,"
"música,tecnologia];[casaco de inverno,22,moda,1]"
)
ficheiro_temporario = tmp_path / "exemplo.txt"
with open(ficheiro_temporario, "w+") as fp:
fp.write(exemplo)
feira.importar_utilizadores(ficheiro_temporario)
assert len(feira.utilizadores) == 10 and [
u.nome for u in feira.utilizadores
] == [
"Ana",
"Manuel",
"Silvia",
"Sofia",
"António",
"Pedro",
"Raquel",
"Paulo",
"Joaquim",
"Cristina",
]
assert all([u.pycoins == 50 for u in feira.utilizadores])
def test_exportar_artigos_preco(self, feira, tmp_path):
"""Testa a exportação de artigos ordenados por quantidade."""
artigo1 = Artigo(nome="Artigo1", preco=1.0, tipologia="Tipo1", quantidade=1)
artigo2 = Artigo(nome="Artigo2", preco=1.0, tipologia="Tipo2", quantidade=2)
artigo3 = Artigo(nome="Artigo3", preco=1.0, tipologia="Tipo3", quantidade=3)
feira.mercado.artigos = [artigo1, artigo2, artigo3]
nome_ficheiro = tmp_path / "artigos_exportados.txt"
# Exportar os artigos
feira.exportar_artigos_preco(nome_ficheiro)
# Verificar se o ficheiro foi criado
assert os.path.exists(nome_ficheiro)
# Lê o conteúdo do ficheiro e verifica se está ordenado por preço
with open(nome_ficheiro, "r") as ficheiro:
conteudo = ficheiro.read()
# Verifica a ordem - menor para maior preço
assert (
conteudo.find("Artigo1")
< conteudo.find("Artigo2")
< conteudo.find("Artigo3")
)
for linha in ficheiro.readlines():
# Verifica o formato
assert re.match(
r"^(\w+,\d+(\.\d+)?,\w+,\d+)", linha
), "A linha não tem o formato correcto"
def test_colocar_artigo_para_venda_novo_artigo(self, feira):
"""Testa adicionar um novo artigo à venda."""
vendedor = Utilizador(
nome="Vendedor1", interesses=["Tech"], artigos_disponiveis=[]
)
artigo_novo = Artigo(
nome="Artigo1", preco=100.0, tipologia="Tipo1", quantidade=5
)
feira.utilizadores.append(vendedor)
feira.colocar_artigo_para_venda("Vendedor1", artigo_novo, 100.0)
assert artigo_novo in vendedor.artigos_disponiveis
assert artigo_novo in feira.mercado.artigos
def test_colocar_artigo_para_venda_preco_ajustado(self, feira):
"""Testa se o preço do artigo é ajustado para o preço menor."""
vendedor = Utilizador(
nome="Vendedor2", interesses=["Moda"], artigos_disponiveis=[]
)
artigo_mercado = Artigo(
nome="Artigo2", preco=80.0, tipologia="Tipo2", quantidade=8
)
artigo_venda = Artigo(
nome="Artigo2", preco=100.0, tipologia="Tipo2", quantidade=3
)
feira.utilizadores.append(vendedor)
feira.mercado.artigos.append(artigo_mercado)
feira.colocar_artigo_para_venda("Vendedor2", artigo_venda, 100.0)
# O preço do artigo vendido deve ser ajustado para 80.0
assert artigo_venda.preco == 80.0
def test_regularizacao_mercado_apos_venda(self, feira):
"""Testa a regularização do mercado após uma venda."""
vendedor = Utilizador(
nome="Vendedor3", interesses=["Livros"], artigos_disponiveis=[]
)
preco = random.randint(1, 100)
artigo = Artigo(
nome="Artigo3",
preco=preco,
tipologia="Tipo3",
quantidade=random.randint(1, 3),
) # Quantidade <= 3
feira.utilizadores.append(vendedor)
feira.colocar_artigo_para_venda("Vendedor3", artigo, preco)
# O preço do artigo deve aumentar em 25% devido à regularização do mercado
preco_esperado = preco * 1.25
assert artigo.preco == preco_esperado
class TestMercado:
@pytest.fixture
def mercado(self):
mercado = Mercado(artigos=[])
return mercado
def test_adicionar_artigo_novo(self, mercado):
"""Testa a adição de um novo artigo ao mercado."""
nome_aletorio = _random_string(random.randint(1, 100))
artigo_novo = Artigo(
nome=nome_aletorio, preco=10.0, tipologia="Tipo1", quantidade=1
)
mercado.adicionar_artigo(artigo_novo)
assert len(mercado.artigos) == 1
assert mercado.artigos[0].nome == nome_aletorio
assert mercado.artigos[0].quantidade == 1
def test_adicionar_artigo_existente(self, mercado):
"""Testa a adição de quantidade a um artigo já existente no mercado."""
nome_aletorio = _random_string(random.randint(1, 100))
tipo_aletorio = _random_string(random.randint(1, 100))
artigo_existente = Artigo(
nome=nome_aletorio, preco=15.0, tipologia=tipo_aletorio, quantidade=2
)
mercado.adicionar_artigo(artigo_existente)
# Adiciona novamente o mesmo artigo com quantidade adicional
artigo_adicional = Artigo(
nome=nome_aletorio, preco=15.0, tipologia=tipo_aletorio, quantidade=3
)
mercado.adicionar_artigo(artigo_adicional)
assert len(mercado.artigos) == 1 # Deve continuar com um artigo apenas
# Quantidade deve ser a soma das duas adições
assert mercado.artigos[0].quantidade == 5
def test_nomes_artigos(self, mercado):
"""Testa se a propriedade nomes_artigos retorna os nomes dos artigos corretamente."""
nome1, nome2 = (
_random_string(random.randint(1, 100)),
_random_string(random.randint(1, 100)),
)
tipo = _random_string(random.randint(1, 100))
artigo1 = Artigo(nome=nome1, preco=20.0, tipologia=tipo, quantidade=1)
artigo2 = Artigo(nome=nome2, preco=25.0, tipologia=tipo, quantidade=2)
mercado.adicionar_artigo(artigo1)
mercado.adicionar_artigo(artigo2)
nomes_esperados = {nome1, nome2}
assert set(mercado.nomes_artigos) == {n.casefold() for n in nomes_esperados}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment