Message!
This is an old work, for updated version please visit Updated FastAPI Course
You might have already noticed that we have several failing unit tests. It's because now our fastapi app expects a valid jwt token to be present in the header of some requests. But, our tests have not adapted to the change. Let's modify the tests to make post and delete requests with a valid jwt header.
Remember, our unit test configurations live at tests > conftest.py. Time to add a new function to get a valid jwt token for requests.
...
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker,Session #new
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# this is to include backend dir in sys.path so that we can import from db,main.py
from db.base import Base
from db.session import get_db
from apis.base import api_router
from core.config import settings #new
from tests.utils.users import authentication_token_from_email #new
...
@pytest.fixture(scope="module")
def client(
app: FastAPI, db_session: SessionTesting
) -> Generator[TestClient, Any, None]:
"""
Create a new FastAPI TestClient that uses the `db_session` fixture to override
the `get_db` dependency that is injected into routes.
"""
def _get_test_db():
try:
yield db_session
finally:
pass
.....
.....
@pytest.fixture(scope="module") #new function
def normal_user_token_headers(client: TestClient, db_session: Session):
return authentication_token_from_email(
client=client, email=settings.TEST_USER_EMAIL, db=db_session
)
Notice that we require a TEST_USER_EMAIL from config file. Let's modify our core > config.py file
class Settings:
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # in mins
TEST_USER_EMAIL = "[email protected]" #new
settings = Settings()
Basically, we have made the normal_user_token_header function a module-level fixture. It will be called once for our use-case and will give us a jwt token. Time to implement the logic to create a get a jwt token during tests. Make a directory/folder inside tests folder and name it utils. We are going to store our test utilities in this folder. Create a new file tests > utils > users.py
from db.repository.users import create_new_user
from db.repository.users import get_user_by_email
from fastapi.testclient import TestClient
from schemas.users import UserCreate
from sqlalchemy.orm import Session
def user_authentication_headers(client: TestClient, email: str, password: str):
data = {"username": email, "password": password}
r = client.post("/login/token", data=data)
response = r.json()
auth_token = response["access_token"]
headers = {"Authorization": f"Bearer {auth_token}"}
return headers
def authentication_token_from_email(client: TestClient, email: str, db: Session):
"""
Return a valid token for the user with given email.
If the user doesn't exist it is created first.
"""
password = "random-passW0rd"
user = get_user_by_email(email=email, db=db)
if not user:
user_in_create = UserCreate(username=email, email=email, password=password)
user = create_new_user(user=user_in_create, db=db)
return user_authentication_headers(client=client, email=email, password=password)
We have created a new function named get_user_by_emil to find out if we already have an existing user with the same email. I just realized we can improve our user creation process by first verifying if a user exists with the same email. Back to the track, we don't have this get_user_by_email function, so, lets create one in db > repository > users.py
def create_new_user(user: UserCreate, db: Session):
...
return user
def get_user_by_email(email:str,db:Session): #new
user = db.query(User).filter(User.email == email).first()
return user
Now, we good to modify our unit tests to include a header if required. Since, we require a header to post job and to delete job. I am going to modify them in unit tests in tests > test_routes > test_jobs.py
def test_create_job(client,normal_user_token_headers): #added normal_user_token_headers
data = {
"title": "SDE super",
"company": "doogle",
"company_url": "www.doogle.com",
"location": "USA,NY",
"description": "python",
"date_posted": "2022-03-20",
}
response = client.post("/jobs/create-job/",data=json.dumps(data),headers=normal_user_token_headers) #added header in the post request
assert response.status_code == 200
assert response.json()["company"] == "doogle"
assert response.json()["description"] == "python"
# We need to modify each and every unit test in which we are making a post/delete request. Since we are not restricting get requests. We do not need headers for get requests.
Once we make the changes, all the tests should pass again. Note, we can also use print statements/logs in tests and they will be printed on stdout if we have failing tests.
Git commit : https://github.com/nofoobar/JobBoard-Fastapi/commit/81f5f1629ea7b9cd42a6c04052b4374c9fc24ef2
Brige the gap between Tutorial hell and Industry. We want to bring in the culture of Clean Code, Test Driven Development.
We know, we might make it hard for you but definitely worth the efforts.
© Copyright 2022-23 Team FastAPITutorial