refactored module to use class structure with a better separation between unit and integration tests; rewrote existing tests, added new ones

This commit is contained in:
eclipse 2025-07-26 18:39:24 +02:00
parent 67259d689b
commit 7cad11981e
2 changed files with 206 additions and 60 deletions

View File

@ -1,26 +1,83 @@
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
import pytest
from the_works.database import db
from the_works.models import Genre
def test_genre_create(client, _app):
"""Integrated testing of adding a Genre record."""
response = client.post("/genre/create", data={"form_Genre": "spam"}, follow_redirects=True)
# assert there was exactly 1 redirect
assert len(response.history) == 1
# assert the redirect led to the correct page
assert response.request.path == "/genre/all"
assert response.status_code == 200
class TestIntGenreRead:
"""Integrated tests for reading all records."""
# assert record was successfully added to DB
with _app.app_context():
genre = db.session.scalars(select(Genre).where(Genre.Genre == "spam")).all()
assert len(genre) == 1
assert isinstance(genre[0], Genre)
def test_records_read(self, client, db):
"""test reading records"""
response = client.get("/genre/all", follow_redirects=True)
# good status code
assert response.status_code == 200
# two records returned
assert response.data.count(b'<tr id="genre-') == 2
# assert records were correctly read from DB
assert b'spam' in response.data
assert b'eggs' in response.data
# assert uniqueness of records
with pytest.raises(IntegrityError) as excinfo:
response = client.post("/genre/create", data={"form_Genre": "spam"})
assert "UNIQUE constraint failed" in str(excinfo.value)
class TestIntGenreCreate:
"""Integrated tests for adding a Genre record."""
def test_record_added(self, client, db):
"""test record creation"""
client.post("/genre/create", data={"form_Genre": "spam & eggs"}, follow_redirects=True)
genres = db.session.scalars(select(Genre).where(Genre.Genre == "spam & eggs")).all()
assert len(genres) == 1
assert isinstance(genres[0], Genre) and genres[0].Genre == "spam & eggs"
def test_uniqueness_constraint(self, client, db):
"""assert uniqueness constraint when adding records"""
with pytest.raises(IntegrityError) as excinfo:
client.post("/genre/create", data={"form_Genre": "spam"})
assert "UNIQUE constraint failed" in str(excinfo.value)
def test_notempty_constraint(self, client):
"""assert non-empty constraint when adding record"""
with pytest.raises(IntegrityError) as excinfo:
client.post("/genre/create", data={"form_Genre": ""})
assert "CHECK constraint failed" in str(excinfo.value)
class TestIntGenreUpdate:
"""Integrated tests for updating a Genre record."""
def test_record_updated(self, client, db):
"""test record updating"""
# get test record id
g = db.session.scalar(select(Genre).where(Genre.Genre == "spam"))
# update record
client.post(f"/genre/update/{g.ID}", data={"form_Genre": "spam & eggs"})
# get updated record
g = db.session.get(Genre, g.ID)
assert g.Genre == "spam & eggs"
def test_uniqueness_constraint(self, client, db):
"""assert uniqueness constraint when updating records"""
g = db.session.scalar(select(Genre).where(Genre.Genre == "spam"))
with pytest.raises(IntegrityError) as excinfo:
client.post(f"/genre/update/{g.ID}", data={"form_Genre": "eggs"})
assert "UNIQUE constraint failed" in str(excinfo.value)
def test_notempty_constraint(self, client, db):
"""assert non-empty constraint when updating record"""
g = db.session.scalar(select(Genre).where(Genre.Genre == "spam"))
with pytest.raises(IntegrityError) as excinfo:
client.post(f"/genre/update/{g.ID}", data={"form_Genre": ""})
assert "CHECK constraint failed" in str(excinfo.value)
class TestIntGenreDelete:
"""Integrated tests for deleting a Genre record."""
def test_record_deleted(self, client, db):
"""test record deletion"""
# get test record id
g = db.session.scalar(select(Genre).where(Genre.Genre == "spam"))
# delete record
client.get(f"/genre/delete/{g.ID}")
g = db.session.scalars(select(Genre).where(Genre.Genre == "spam")).all()
assert g == []

View File

@ -1,58 +1,147 @@
import pytest
from the_works.models import Genre
def test_genre_all(client, mocker):
class TestUnitGenreRead:
"""Test view all() from genre.py."""
# mock database function
# Note: The original scalars() method returns an sqlalchemy.engine.Result.ScalarResult, not a list
# but the template code uses the return value in a way which works for both ScalarResult and list
mocker.patch("flask_sqlalchemy.session.Session.scalars", return_value=[
Genre(ID=4, Genre="spam"),
Genre(ID=26, Genre="eggs")
])
def test_get_request(self, client, mocker):
"""test GET request"""
# test case: get request
response = client.get("/genre")
assert response.status_code == 200
assert response.data.count(b'<tr id="genre-') == 2
# mock database function
# Note: scalars() should return an sqlalchemy.engine.Result.ScalarResult, not a list
# but the template code uses the return value in a way which works for both ScalarResult and list
mocker.patch("flask_sqlalchemy.session.Session.scalars", return_value=[
Genre(ID=4, Genre="SpAm"),
Genre(ID=26, Genre="eGgS")
])
response = client.get("/genre")
# assert good HTTP response
assert response.status_code == 200
# assert two records read
assert response.data.count(b'<tr id="genre-') == 2
# assert records were correctly read from DB
assert b'SpAm' in response.data
assert b'eGgS' in response.data
# test case: post request
response = client.post("/genre")
assert response.status_code == 405
def test_get_request_with_query(self, client):
response = client.get("/genre", query_string={"id": 1, "form_genre": "spam"})
# assert good HTTP response
assert response.status_code == 200
def test_post_request(self, client):
"""test POST request"""
response = client.post("/genre")
assert response.status_code == 405
def test_genre_create(client, mocker):
class TestUnitGenreCreate:
"""Test view create() from genre.py."""
# mock database function
mocker.patch("flask_sqlalchemy.session.Session.add")
def test_good_data_request(self, client, mocker):
"""test a POST request with good data"""
mocker.patch("flask_sqlalchemy.session.Session.add")
response = client.post("/genre/create", data={"form_Genre": "spam"}, follow_redirects=True)
assert response.status_code == 200
# test a POST request with good data
response = client.post("/genre/create", data={"form_Genre": "spam"}, follow_redirects=True)
# exactly 1 redirect
assert len(response.history) == 1
# redirect to the right page
assert response.request.path == "/genre/all"
assert response.status_code == 200
def test_no_data(self, client, mocker):
"""test a POST request with no form data"""
mocker.patch("flask_sqlalchemy.session.Session.add")
response = client.post("/genre/create", data={}, follow_redirects=True)
assert response.status_code == 200 # note: a real DB would have raised an IntegrityError
# test a POST request with no form data
with pytest.raises(ValueError) as excinfo:
response = client.post("/genre/create", data={})
assert "value can't be empty" in str(excinfo.value)
def test_bad_data(self, client, mocker):
"""test a POST request with bad form data"""
mocker.patch("flask_sqlalchemy.session.Session.add")
response = client.post("/genre/create", data={"wrong_key": "eggs"}, follow_redirects=True)
assert response.status_code == 200 # same
# test a POST request with bad form data
with pytest.raises(ValueError) as excinfo:
response = client.post("/genre/create", data={"wrong_key": "eggs"})
assert "value can't be empty" in str(excinfo.value)
def test_empty_data(self, client, mocker):
"""test a POST request with empty form data"""
mocker.patch("flask_sqlalchemy.session.Session.add")
response = client.post("/genre/create", data={"form_genre": ""}, follow_redirects=True)
assert response.status_code == 200 # same
# test a POST request with empty form data
with pytest.raises(ValueError) as excinfo:
response = client.post("/genre/create", data={"form_genre": ""})
assert "value can't be empty" in str(excinfo.value)
def test_get_request(self, client):
"""test a GET request"""
response = client.get("/genre/create", query_string={"form_Genre": "spam & eggs"})
assert response.status_code == 405
# test a GET request
response = client.get("/genre/create", query_string={"form_Genre": "spam eggs"})
assert response.status_code == 405
class TestUnitGenreUpdate:
"""Test view update() from genre.py."""
def test_good_data_request(self, client, mocker):
"""test a POST request with good data"""
mocker.patch("flask_sqlalchemy.session.Session.get", return_value=Genre(ID=1, Genre="spam"))
response = client.post("/genre/update/1", data={"form_Genre": "eGgS"}, follow_redirects=True)
assert response.status_code == 200
def test_no_data(self, client, mocker):
"""test a POST request with no form data"""
mocker.patch("flask_sqlalchemy.session.Session.get", return_value=Genre(ID=1, Genre="spam"))
response = client.post("/genre/update/1", data={}, follow_redirects=True)
assert response.status_code == 200
def test_bad_data(self, client, mocker):
"""test a POST request with bad form data"""
# mock database function
mocker.patch("flask_sqlalchemy.session.Session.get", return_value=Genre(ID=1, Genre="spam"))
response = client.post("/genre/update/1", data={"wrong_key": "eggs"}, follow_redirects=True)
assert response.status_code == 200
def test_empty_data(self, client, mocker):
"""test a POST request with empty form data"""
# mock database function
mocker.patch("flask_sqlalchemy.session.Session.get", return_value=Genre(ID=1, Genre="spam"))
response = client.post("/genre/update/1", data={"form_genre": ""}, follow_redirects=True)
assert response.status_code == 200
def test_no_id(self, client):
"""test POST request without id"""
response = client.post("/genre/update/", data={"form_Genre": "spam & eggs"}, follow_redirects=True)
assert response.status_code == 404
def test_bad_id(self, client, mocker):
"""test method call with an id that doesn't exist"""
mocker.patch("flask_sqlalchemy.session.Session.get", return_value=None)
response = client.post("/genre/update/123", data={"form_Genre": "spam & eggs"}, follow_redirects=True)
assert response.status_code == 200
def test_get_request(self, client):
"""test a GET request"""
response = client.get("/genre/update/1", query_string={"form_Genre": "spam & eggs"})
assert response.status_code == 405
class TestUnitGenreDelete:
"""Test view delete() from genre.py."""
def test_get_request(self, client, mocker):
"""test a GET request with good data"""
mocker.patch("flask_sqlalchemy.session.Session.get", return_value=Genre(ID=1, Genre="spam"))
mocker.patch("flask_sqlalchemy.session.Session.delete")
response = client.get("/genre/delete/1", follow_redirects=True)
assert response.status_code == 200
def test_get_request_with_query(self, client, mocker):
"""test a GET request with good data"""
mocker.patch("flask_sqlalchemy.session.Session.get", return_value=Genre(ID=1, Genre="spam"))
mocker.patch("flask_sqlalchemy.session.Session.delete")
response = client.get("/genre/delete/1", query_string={"spam": 1, "eggs": "important stuff"}, follow_redirects=True)
assert response.status_code == 200
def test_no_id(self, client):
"""test a GET request with no id"""
response = client.get("/genre/delete", follow_redirects=True)
assert response.status_code == 404
def test_bad_id(self, client, mocker):
"""test a GET request with a non-existing id"""
mocker.patch("flask_sqlalchemy.session.Session.delete")
response = client.get("/genre/delete/123", follow_redirects=True)
assert response.status_code == 200
def test_post_request(self, client):
"""test a POST request"""
response = client.post("/genre/delete/1")
assert response.status_code == 405