简体   繁体   中英

FastAPI testing No ORM Databases

I am a None ORM user, I prefer to use plain text queries, I took that course on free code camp on FastAPI but almost the whole course revolves around ORMs so I struggle to implement a number of concepts found in that course.

My main struggle is when I write tests for my endpoints I need to use a test database rather than my development database, I am using testing.postgres to create the testing database and this is the code I am using:

client = TestClient(app)

class TestUser:
    def setup_method(self, method):
        self.postgres = testing.postgresql.Postgresql(cache_initialized_db=True)
        self.db = psycopg2.connect(**self.postgres.dsn())

    def teardown_method(self, method):
        self.db.close()

    def test_get_users(self):
        res = client.get("/users")
        assert res.status_code == 200
        assert res.headers["content-type"] == "application/json"

my question is how to make my tests and cruds send queries to the test database, not the development database.

where is the link that I should add here?

Edit: this is the code I use for connection with database, I am not using a dependency for it:

while True:
    try:
        conn = psycopg2.connect(
            database=settings.database,
            user=settings.user,
            port=settings.port,
            password=settings.password,
            cursor_factory=RealDictCursor,
        )
        cr = conn.cursor()
        console.print(
            "[green bold]SUCCESS[/]:    Connection To Database Established successfuly"
        )
        break
    except psycopg2.OperationalError:
        console.print(
            "[red bold]FAILED[/]:    Connection To Database Failed , Trying Again"
        )
        time.sleep(2)

Edit2: this is the settings segment:

load_dotenv()


class Settings(BaseSettings):
    database: str
    user: str
    port: int
    password: str
    expiray: int
    algorithm: str
    secret_key: str


settings = Settings()

If you wanna use the test database rather than the development database you should create the testing database and let the backend application be able to find the testing database and use it.

Kareem, I suggest you create your Settings instance from one or another .env file, based on an ENVIRONMENT variable:

import os
import sys

import pydantic


class Settings(pydantic.BaseSettings):
    database: str
    user: str
    port: int
    password: str
    expiray: int
    algorithm: str
    secret_key: str


try:
    ENVIRONMENT = os.environ['ENVIRONMENT']
except KeyError:
    print('make sure to set an "ENVIRONMENT" env var')
    sys.exit()

    
settings = Settings(_env_file=f'{ENVIRONMENT}.env', _env_file_encoding='utf-8')

This way, if you want to work with the testing database, you can do $ export ENVIRONMENT=test and it will load the settings from a file called test.env .

Does that make sense? Here are the docs for pydantic 's dotenv support for BaseSettings subclasses.

As an aside, since you're interested in doing as much as you can without the use of an ORM and perhaps other dependencies, you really don't need pydantic for your purposes: You could simply change the above to something like:


import os
import sys
import types

import dotenv


try:
    ENVIRONMENT = os.environ['ENVIRONMENT']
except KeyError:
    print('make sure to set an "ENVIRONMENT" env var')
    sys.exit()


db_settings_dict = dotenv.dotenv_values(f"{ENVIRONMENT}.env")

# to get dot notation as in your example
settings = types.SimpleNamespace(**db_settings_dict)

where test.env contains things like

database=hey.there.com:8768
user=kareem
...

Below is the Script I was written to control the ENV variable from the.env file, before running the unicorn server you would load the.env file based on the ENV mode to determine what kinds of modes are working on then use conditions to set up the corresponding DB path and name.


import os
import sys
from colored import fg
from os import environ, path
from dotenv import load_dotenv, set_key
import dotenv


def unknown(option, args=None):
    color = fg('red')
    if args:
        print(color + f'Unknown arguments: -{args}')
    else:
        print(color + f'Unknown option: -{option}')
    print("usage: python3 config.py [option] [arguments]")
    print("Try `python3 config.py --help for more information")
    color = fg('white')
    print(color)


def version():
    basedir = path.abspath(path.dirname(__file__))
    load_dotenv(path.join(basedir, '.env'))
    color = fg('blue')
    print(color + '[VERSION]: ' + environ.get('VER'))
    color = fg('white')
    print(color)


def env():
    basedir = path.abspath(path.dirname(__file__))
    load_dotenv(path.join(basedir, '.env'))
    color = fg('blue')
    print(color + '[ENV]: ' + environ.get('ENV'))
    color = fg('white')
    print(color)


def set_env(env):
    dotenv_file = dotenv.find_dotenv()

    basedir = path.abspath(path.dirname(__file__))
    load_dotenv(path.join(basedir, '.env'))
    color = fg('blue')
    print(color + f"[Updating ENV] ... ... from {environ.get('ENV')} to {env}")
    os.environ["ENV"] = env
    set_key(dotenv_file, 'ENV', os.environ['ENV'])
    print("[Successfully Update the 'ENV' from .env]")
    color = fg('white')
    print(color)


def doc():
    color = fg('blue')
    print(color + "usage: python3 config.py [option] [arguments]")

    read_me = """
    -v                  : current project version
    -env                : current project environment
    -set ENV={env}      : set project env 'DEV' or 'PROD'
    """
    print(read_me)
    color = fg('white')
    print(color)


def action_handler(option, args):
    if option == '-set':
        args_list = args.split('=')
        if args_list[0] == 'ENV' and (args_list[1] == 'DEV' or args_list[1] == 'PROD'):
            set_env(args_list[1])
        else:
            unknown(option=option, args=args)
    else:
        unknown(option=option)


argvs = len(sys.argv)

if argvs == 1:
    doc()
elif argvs == 2:
    option = sys.argv[1]
    if option == '--help':
        doc()
    elif option == '-v':
        version()
    elif option == '-env':
        env()
    else:
        unknown(option)
elif argvs == 3:
    option, args = sys.argv[1], sys.argv[2]
    action_handler(option, args)
else:
    pass

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM