Pytest
Pytest-sovelluskehys mahdollistaa helpon tavan kirjoittaa testejä python-sovelluksille. Pytestin avulla voi testata myös Flask-sovelluksia. Tarkemmin aihetta opetetaan TEKS5466 Ohjelmistotestaus -kurssilla.
- Asenna pytest
pip install -U pytest - Lue Get Started.
Kirjoita testi tiedostoon, jonka nimi alkaa sanalla test_. Esim. test_malli.py
#testattava funktio def omafunktio(a): return 2*a #pytestin testifunktio def test_omafunktio(): assert omafunktio(2) == 1, "Testi epäonnistui" - Suorita testi kirjoittamalla joko pytest tai python -m pytest
[venv] [tjlahton@jalava pytest]$ pytest ================================================= test session starts ================================================== platform linux -- Python 3.8.14, pytest-7.2.1, pluggy-1.0.0 rootdir: /autowww/itsovpa2/appro.mit.jyu.fi-https/ties4080/luennot/pytest collected 1 item test_malli.py F [100%] ======================================================= FAILURES ======================================================= ___________________________________________________ test_omafunktio ____________________________________________________ def test_omafunktio(): > assert omafunktio(2) == 1, "Testi epäonnistui" E AssertionError: Testi epäonnistui E assert 4 == 1 E + where 4 = omafunktio(2) test_malli.py:7: AssertionError =============================================== short test summary info ================================================ FAILED test_malli.py::test_omafunktio - AssertionError: Testi epäonnistui ================================================== 1 failed in 0.04s =================================================== [venv] [tjlahton@jalava pytest]$ - Jos haluat lyhyemmän listauksen tuloksista:
[venv] [tjlahton@jalava pytest]$ pytest --tb=line ================================================= test session starts ================================================== platform linux -- Python 3.8.14, pytest-7.2.1, pluggy-1.0.0 rootdir: /autowww/itsovpa2/appro.mit.jyu.fi-https/ties4080/luennot/pytest collected 1 item test_malli.py F [100%] ======================================================= FAILURES ======================================================= /autowww/itsovpa2/appro.mit.jyu.fi-https/ties4080/luennot/pytest/test_malli.py:7: AssertionError: Testi epäonnistui =============================================== short test summary info ================================================ FAILED test_malli.py::test_omafunktio - AssertionError: Testi epäonnistui ================================================== 1 failed in 0.02s =================================================== [venv] [tjlahton@jalava pytest]$ - Yleensä testit kannattaa sijoittaa omaan tests-kansioon, josta pytest osaa ne automaattisesti etsiä. Good Integration Practices
Flask-sovellusten testaaminen
Flask-sovellusten testaamisesta kerrotaan tarkemmin dokumentissa Testing Flask Applications.
Kirjoitetaan pieni esimerkkisovellus oma.py:
from flask import Flask, Response, request
import json
import random
app = Flask(__name__)
#muista mahdolliset ongelmat globaalien muuttujien kanssa
nimet = ["foo", "bar", "foobar"]
@app.route('/', methods=["GET"])
def listaa():
#tässä haettaisiin nimet jostain ja lopulta palautettaisiin html-koodia, jossa nimet ovat
return "foo, bar ja foobar"
@app.route('/lisaa', methods=["POST"])
def lisaa():
#lisättäisiin uusi nimi esim. tietokantaan ja palautetaan tieto siitä onnistuiko lisäys vai ei
#lisätään nyt vain globaaliin listaan
global nimet
nimi = request.values.get("nimi", "-")
#palautetaan satunnainen tulos vaikka lisäys oikeasti aina onnistuisikin
if random.choice([True, False]):
nimet.append(nimi)
return "Onnistui lisätä " + nimi
else:
return "Epäonnistui lisätä " + nimi
@app.route('/listaa_json', methods=["GET"])
def listaa_json():
global nimet
#tässä haettaisiin nimet jostain ja lopulta palautettaisiin nimet json-muodossa
#tässä tulee mukana myös edellä lisätty, koska lista on globaalissa muuttujassa ja sama sovellus
#elää kaikkien testien ajan. pytest ajaa testit tiedostossa olevassa järjestyksessä
return Response(json.dumps(nimet), mimetype="application/json")
Edellistä sovellusta voitaisiin testata esimerkiksi seuraavilla tavoilla (test_oma.py):
from flask import Flask, Response
import pytest
import json
#ladataan oma sovelluskoodi
import oma
#alustetaan pytestiä varten tarvittavat funktiot
@pytest.fixture()
def app():
#käytetään omassa sovelluksessa määriteltyä app-muuttujaa
app = oma.app
# other setup can go here
# varo mahdollisia globaaleja muuttujia
# ne voi tarvittaessa nollata täällä
# oma.nimet = []
yield app
# clean up / reset resources here
#suoritetaan flaskin oma test-klientti
@pytest.fixture()
def client(app):
return app.test_client()
#varsinaiset testit joille pytest tuo tässä tarvittavan klientin parametrina
def test_listaa1(client):
response = client.get("/")
#testataan http-koodi, 200 == ok
assert 200 == response.status_code, 'Sovelluksen listaussivu ei toimi'
#samaan tapaan voisi testata myös muita http-koodeja
def test_listaa2(client):
url = "/"
# jos haluttaisiin viedä query_stringin mukana parametreja, niin käytettäisiin samanlaista dictiä
# kuin seuraavassa post-metodin yhteydessä. kutsu muuttuisi muotoon client.get(url, query_string=data)
response = client.get(url)
#response.data on se mitä flask-sovellus palauttaa. Data on oletuksena utf-8-koodattua binaaridataa eli aina pitää ensin ajaa decode
data = response.data.decode("UTF-8")
#data on nyt html-koodia ja yksinkertaisesti vain etsitään löytyykö kukin merkkijono sivulta
assert all(nimi in data for nimi in ["foo", "bar", "foobar"]), 'Palautetut nimet ovat väärät' + response.data.decode("UTF-8")
def test_lisaa(client):
url = "/lisaa"
#alustetaan post-data eli vastaavat tiedot kuin tulisivat esim. lomakkeelta
reqdata = {
"nimi": "barfoo",
}
#huomaa, että käytetään nyt client.post eikä client.get
response = client.post(url, data=reqdata)
#Kokeillaan onko palautettu data haluttu
assert "Onnistui" in response.data.decode("UTF-8"), "Lisääminen epäonnistui : " + response.data.decode("UTF-8")
def test_listaajson(client):
url = "/listaa_json"
response = client.get(url)
data = json.loads(response.data)
#data on nyt json-muodossa tullut pythonin lista. Sama testi kuin edellä toimii tässäkin, mutta tämä on toki tarkempi
assert all(nimi in data for nimi in ["foo", "bar", "foobar", "barfoo"]), 'Palautetut nimet ovat väärät : ' + response.data.decode("UTF-8")
Suoritetaan testit. Ne onnistuvat satunnaisesti:
[venv] [tjlahton@jalava pytest]$ python -m pytest test_malli.py
================================================= test session starts ==================================================
platform linux -- Python 3.8.14, pytest-7.2.1, pluggy-1.0.0
rootdir: /autowww/itsovpa2/appro.mit.jyu.fi-https/ties4080/luennot/pytest
collected 4 items
test_malli.py .... [100%]
================================================== 4 passed in 0.14s ===================================================
[venv] [tjlahton@jalava pytest]$
Epäonnistunut tulos:
============================= test session starts ==============================
platform linux -- Python 3.8.14, pytest-7.2.1, pluggy-1.0.0
rootdir: /autowww/itsovpa2/appro.mit.jyu.fi-https/ties4080/luennot/pytest
collected 4 items
test_malli.py ..FF [100%]
=================================== FAILURES ===================================
__________________________________ test_lisaa __________________________________
client = <FlaskClient <Flask 'oma'>>
def test_lisaa(client):
url = "/lisaa"
#alustetaan post-data eli vastaavat tiedot kuin tulisivat esim. lomakkeelta
reqdata = {
"nimi": "barfoo",
}
response = client.post(url, data=reqdata)
#Kokeillaan onko palautettu data haluttu
> assert "Onnistui" in response.data.decode("UTF-8"), "Lisääminen epäonnistui : " + response.data.decode("UTF-8")
E AssertionError: Lisääminen epäonnistui : Epäonnistui lisätä barfoo
E assert 'Onnistui' in 'Epäonnistui lisätä barfoo'
E + where 'Epäonnistui lisätä barfoo' = <built-in method decode of bytes object at 0x7fae6e603830>('UTF-8')
E + where <built-in method decode of bytes object at 0x7fae6e603830> = b'Ep\xc3\xa4onnistui lis\xc3\xa4t\xc3\xa4 barfoo'.decode
E + where b'Ep\xc3\xa4onnistui lis\xc3\xa4t\xc3\xa4 barfoo' = <WrapperTestResponse 28 bytes [200 OK]>.data
test_malli.py:46: AssertionError
_______________________________ test_listaajson ________________________________
client = <FlaskClient <Flask 'oma'>>
def test_listaajson(client):
url = "/listaa_json"
response = client.get(url)
data = json.loads(response.data)
#data on nyt json-muodossa tullut pythonin lista. Sama testi kuin edellä toimii tässäkin
> assert all(nimi in data for nimi in ["foo", "bar", "foobar", "barfoo"]), 'Palautetut nimet ovat väärät : ' + response.data.decode("UTF-8")
E AssertionError: Palautetut nimet ovat väärät : ["foo", "bar", "foobar"]
E assert False
E + where False = all(<generator object test_listaajson.<locals>.<genexpr> at 0x7fae6e61e7b0>)
test_malli.py:54: AssertionError
=========================== short test summary info ============================
FAILED test_malli.py::test_lisaa - AssertionError: Lisääminen epäonnistui : E...
FAILED test_malli.py::test_listaajson - AssertionError: Palautetut nimet ovat...
========================= 2 failed, 2 passed in 0.19s ==========================
Käyttäjien kommentit