I just started using FastAPI but I do not know how do I write a unit test (using pytest) for a Pydantic model.
Here is a sample Pydantic model:
class PhoneNumber(BaseModel):
id: int
country: str
country_code: str
number: str
extension: str
I want to test this model by creating a sample PhoneNumber
instance and ensure that the PhoneNumber
instance tallies with the field types. For example:
PhoneNumber(1, "country", "code", "number", "extension")
Then, I want to assert that PhoneNumber.country equals "country".
The test you want to achieve is straightforward to do with pytest:
import pytest
def test_phonenumber():
pn = PhoneNumber(id=1, country="country", country_code="code", number="number", extension="extension")
assert pn.id == 1
assert pn.country == 'country'
assert pn.country_code == 'code'
assert pn.number == 'number'
assert pn.extension == 'extension'
But I agree with this comment :
Generally speaking, you don't write tests like this. Pydantic has a good test suite (including a unit test like the one you're proposing ). Your test should cover the code and logic you wrote, not the packages you imported.
If you have a model like PhoneNumber
model without any special/complex validations, then writing tests that simply instantiates it and checks attributes won't be that useful. Tests like those are like testing Pydantic itself.
If, however, your model has some special/complex validator functions , for example, it checks if country
and country_code
match:
from pydantic import BaseModel, root_validator
class PhoneNumber(BaseModel):
...
@root_validator(pre=True)
def check_country(cls, values):
"""Check that country_code is the 1st 2 letters of country"""
country: str = values.get('country')
country_code: str = values.get('country_code')
if not country.lower().startswith(country_code.lower()):
raise ValueError('country_code and country do not match')
return values
...then a unit test for that specific behavior would be more useful:
import pytest
def test_phonenumber_country_code():
"""Expect test to fail because country_code and country do not match"""
with pytest.raises(ValueError):
PhoneNumber(id=1, country='JAPAN', country_code='XY', number='123', extension='456')
Also, as I mentioned in my comment , since you mentioned FastAPI, if you are using this model as part of a route definition (either it's a request parameter or a response model), then a more useful test would be making sure that your route can use your model correctly.
@app.post("/phonenumber")
async def add_phonenumber(phonenumber: PhoneNumber):
"""The model is used here as part of the Request Body"""
# Do something with phonenumber
return JSONResponse({'message': 'OK'}, status_code=200)
from fastapi.testclient import TestClient
client = TestClient(app)
def test_add_phonenumber_ok():
"""Valid PhoneNumber, should be 200/OK"""
# This would be what the JSON body of the request would look like
body = {
"id": 1,
"country": "Japan",
"country_code": "JA",
"number": "123",
"extension": "81",
}
response = client.post("/phonenumber", json=body)
assert response.status_code == 200
def test_add_phonenumber_error():
"""Invalid PhoneNumber, should be a validation error"""
# This would be what the JSON body of the request would look like
body = {
"id": 1,
"country": "Japan",
# `country_code` is missing
"number": 99999, # `number` is int, not str
"extension": "81",
}
response = client.post("/phonenumber", json=body)
assert response.status_code == 422
assert response.json() == {
'detail': [{
'loc': ['body', 'country_code'],
'msg': 'field required',
'type': 'value_error.missing'
}]
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.