Co definiuje dobry test?

Co definiuje dobry test?
Piotrek Małpa
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5
0

Mam ciągłe wrażenie, że coś mogę zrobić lepiej w swoich testach np. API.
Przykładowo mam coś takiego
conftest:

Kopiuj
import pytest
import requests
from faker import Faker
from sqlmodel import Session, select, delete
from passlib.context import CryptContext
from database import engine
from models import User
import time
fake = Faker()
from auth import get_password_hash
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

@pytest.fixture
def passhash():
    p = get_password_hash('admminpass123')
    return p
# --- Base URL ---

@pytest.fixture
def base():
    return "http://127.0.0.1:8000"

    # --- Klasa dla użytkownika ---

class APIUser:
    
    def __init__(self, base):
        self.base = base
        self.username = fake.user_name()
        self.password = fake.password()
        self.email = fake.email()



    def register(self):
            return requests.post(
                f"{self.base}/users/register",
                json={"username": self.username, "password": self.password, "email": self.email},
            )

    def login(self):
        return requests.post(
            f"{self.base}/users/token",
            json={"username": self.username, "password": self.password, "email": self.email},
        )



# --- Fixture dla admina ---



@pytest.fixture
def headers(base):
    u = APIUser(base)
    u.register()
    login = u.login()
    token = login.json()["access_token"]
    return {'Authorization': f'Bearer {token}'}


# --- Klasa Projects ---

class Pro:
    def __init__(self, base, user_headers):
        self.base = base
        self.head = user_headers

        self.name = fake.user_name()
        self.desc = fake.text()


    def add_projects(self):
        return requests.post(
            f'{self.base}/projects',
            json={'name': self.name, 'description': self.desc},
            headers=self.head
        )

    def get_projects(self):
        return requests.get(f'{self.base}/projects', headers=self.head)

    def get_projects_by_id(self, id):
        return requests.get(f'{self.base}/projects/{id}', headers=self.head)

def add_admin_to_db(passhash):
    #engine
    
    with Session(engine) as ses:
        user = User(username="admiinistrator", email="admmiin1234@gmail.com", hashed_password=passhash, role="admin")
        ses.add(user)
        ses.commit()
        ses.refresh(user)
        assert user.id is not None
@pytest.fixture
def login_admnistartr():
    log = requests.post('http://127.0.0.1:8000/users/token', json={'username': "admiinistrator", 'email':"admmiin1234@gmail.com", 'password': "admminpass123", 'role': 'admin'})
    assert log.status_code == 200
    token = log.json()['access_token']
    head = {'Authorization': f'Bearer {token}'}
    return head



class Pro_admin:
    def __init__(self, base, login_admnistartr):
        self.base = base
        self.head = login_admnistartr

        self.name = fake.user_name()
        self.desc = fake.text()
    def delete_projects(self, id):
        return requests.delete(
            f'{self.base}/projects/{id}',
            headers=self.head
        )

class tasks:
    def __init__(self, base, headers, login_admnistartr):
        self.base = base
        self.title = fake.password()
        self.desc = fake.user_name()
        self.head = headers
        self.admin_head = login_admnistartr
    def add_task(self, id):
        return requests.post(f'{self.base}/projects/{id}/tasks', json={'title': self.title, 'description': self.desc, 'complete': False}, headers=self.head)
    def edit_task(self, id):
        return requests.put(f'{self.base}/tasks/{id}', json={'title': self.title, 'description': self.desc, 'complete': False}, headers=self.admin_head)
    def delete_task(self, id):
        return requests.delete(f'{self.base}/tasks/{id}', headers=self.admin_head)
    
@pytest.fixture
def task(base, headers, login_admnistartr):
    return tasks(base, headers, login_admnistartr)
@pytest.fixture
def pro(base, headers):
    return Pro(base, headers)

@pytest.fixture
def proad(base, login_admnistartr):
    return Pro_admin(base, login_admnistartr)



i testy:

Kopiuj
from conftest import pro, proad, task
def test_add_pro(pro):
    add = pro.add_projects()
    assert add.status_code == 201

    pid = add.json()["id"]
    get = pro.get_projects_by_id(pid)

    assert get.status_code == 200
    assert get.json()["name"] == pro.name

def test_delete(pro, proad):
    add = pro.add_projects()
    pid = add.json()["id"]
    dele = proad.delete_projects(pid)
    assert dele.status_code == 204
    get = pro.get_projects_by_id(pid)
    assert get.json() == {'detail': 'Project not found'}

def test_add_task(task, pro):
    add = pro.add_projects()
    pid = add.json()["id"]
    add_task = task.add_task(pid)
    get = pro.get_projects_by_id(pid)
    assert add_task.status_code == 201
    name = add_task.json()['title']
    for t in get.json()['tasks']:
        assert name in t['title']

def test_edit(task, pro):
    add = pro.add_projects()
    pid = add.json()["id"]
    add_task  = task.add_task(pid)
    tid = add_task.json()['id']
    edit = task.edit_task(tid)
    get = pro.get_projects_by_id(pid)
    print(get.json())
    assert add.json()['tasks'] != get.json()['tasks']

def test_delete(task, pro):
    add = pro.add_projects()
    pid = add.json()["id"]
    add_task  = task.add_task(pid)
    tid = add_task.json()['id']
    dele = task.delete_task(tid)
    get = pro.get_projects_by_id(pid)
    assert get.json()['tasks'] == []


i wiem że mogę coś zrobić lepiej ale nie wiem co więc stąd pytanie co definiuje dobry test?

SL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1020
lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
2

Nie musisz importować fixtures z conftest. Naming masz przedziwny, np., class Pro_admin. Spróbuj jedną asercję na test, albo parametryzować, pozdrawiam debugujących tutaj:

Kopiuj
    for t in get.json()['tasks']:
        assert name in t['title']

Sprawdź, ale wydaje mi się, że testy mogą wołać requstests, np., test_add_task. Znowu naming, test_delete co to testuje i jaki scenariusz? Nie wiedze tam metody delete.
Odpowiadając na pytanie, dobry testy definiuje:

  • testują one tylko logikę kodu, wszystkie zależności są wstrzyknięte, (patchowane ble:));
  • mają jasno zdefiniowany scenariusz, który najlepiej, jak można odczytać z nazwy;
  • no i pokrycie + łatwość debugowania - asercja na test.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.