mirror of
https://github.com/fastapi/sqlmodel.git
synced 2025-08-15 02:07:54 +08:00
📝 Add docs annotations for source examples
This commit is contained in:
@ -0,0 +1,17 @@
|
||||
1. Import the `app` from the the `main` module.
|
||||
|
||||
2. We create a `TestClient` for the FastAPI `app` and put it in the variable `client`.
|
||||
|
||||
3. Then we use use this `client` to **talk to the API** and send a `POST` HTTP operation, creating a new hero.
|
||||
|
||||
4. Then we get the **JSON data** from the response and put it in the variable `data`.
|
||||
|
||||
5. Next we start testing the results with `assert` statements, we check that the status code of the response is `200`.
|
||||
|
||||
6. We check that the `name` of the hero created is `"Deadpond"`.
|
||||
|
||||
7. We check that the `secret_name` of the hero created is `"Dive Wilson"`.
|
||||
|
||||
8. We check that the `age` of the hero created is `None`, because we didn't send an age.
|
||||
|
||||
9. We check that the hero created has an `id` created by the database, so it's not `None`.
|
@ -0,0 +1,25 @@
|
||||
1. Import the `get_session` dependency from the the `main` module.
|
||||
|
||||
2. Define the new function that will be the new **dependency override**.
|
||||
|
||||
3. This function will return a different **session** than the one that would be returned by the original `get_session` function.
|
||||
|
||||
We haven't seen how this new **session** object is created yet, but the point is that this is a different session than the original one from the app.
|
||||
|
||||
This session is attached to a different **engine**, and that different **engine** uses a different URL, for a database just for testing.
|
||||
|
||||
We haven't defined that new **URL** nor the new **engine** yet, but here we already see the that this object `session` will override the one returned by the original dependency `get_session()`.
|
||||
|
||||
4. Then, the FastAPI `app` object has an attribute `app.dependency_overrides`.
|
||||
|
||||
This attribute is a dictionary, and we can put dependency overrides in it by passing, as the **key**, the **original dependency function**, and as the **value**, the **new overriding dependency function**.
|
||||
|
||||
So, here we are telling the FastAPI app to use `get_session_override` instead of `get_session` in all the places in the code that depend on `get_session`, that is, all the parameters with something like:
|
||||
|
||||
```Python
|
||||
session: Session = Depends(get_session)
|
||||
```
|
||||
|
||||
5. After we are done with the dependency override, we can restore the application back to normal, by removing all the values in this dictionary `app.dependency_overrides`.
|
||||
|
||||
This way whenever a *path operation function* needs the dependency FastAPI will use the original one instead of the override.
|
@ -0,0 +1,37 @@
|
||||
1. Here's a subtle thing to notice.
|
||||
|
||||
Remember that [Order Matters](../create-db-and-table.md#sqlmodel-metadata-order-matters){.internal-link target=_blank} and we need to make sure all the **SQLModel** models are already defined and **imported** before calling `.create_all()`.
|
||||
|
||||
IN this line, by importing something, *anything*, from `.main`, the code in `.main` will be executed, including the definition of the **table models**, and that will automatically register them in `SQLModel.metadata`.
|
||||
|
||||
2. Here we create a new **engine**, completely different from the one in `main.py`.
|
||||
|
||||
This is the engine we will use for the tests.
|
||||
|
||||
We use the new URL of the database for tests:
|
||||
|
||||
```
|
||||
sqlite:///testing.db
|
||||
```
|
||||
|
||||
And again, we use the connection argument `check_same_thread=False`.
|
||||
|
||||
3. Then we call:
|
||||
|
||||
```Python
|
||||
SQLModel.metadata.create_all(engine)
|
||||
```
|
||||
|
||||
...to make sure we create all the tables in the new testing database.
|
||||
|
||||
The **table models** are registered in `SQLModel.metadata` just because we imported *something* from `.main`, and the code in `.main` was executed, creating the classes for the **table models** and automatically registering them in `SQLModel.metadata`.
|
||||
|
||||
So, by the point we call this method, the **table models** are already registered there. 💯
|
||||
|
||||
4. Here's where we create the custom **session** object for this test in a `with` block.
|
||||
|
||||
It uses the new custom **engine** we created, so anything that uses this session will be using the testing database.
|
||||
|
||||
5. Now, back to the dependency override, it is just returning the same **session** object from outside, that's it, that's the whole trick.
|
||||
|
||||
6. By this point, the testing **session** `with` block finishes, and the session is closed, the file is closed, etc.
|
@ -0,0 +1,26 @@
|
||||
1. Import `StaticPool` from `sqlmodel`, we will use it in a bit.
|
||||
|
||||
2. For the **SQLite URL**, don't write any file name, leave it empty.
|
||||
|
||||
So, instead of:
|
||||
|
||||
```
|
||||
sqlite:///testing.db
|
||||
```
|
||||
|
||||
...just write:
|
||||
|
||||
```
|
||||
sqlite://
|
||||
```
|
||||
|
||||
This is enough to tell **SQLModel** (actually SQLAlchemy) that we want to use an **in-memory SQLite database**.
|
||||
|
||||
3. Remember that we told the **low-level** library in charge of communicating with SQLite that we want to be able to **access the database from different threads** with `check_same_thread=False`?
|
||||
|
||||
Now that we use an **in-memory database**, we need to also tell SQLAlchemy that we want to be able to use the **same in-memory database** object from different threads.
|
||||
|
||||
We tell it that with the `poolclass=StaticPool` parameter.
|
||||
|
||||
!!! info
|
||||
You can read more details in the <a href="https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#using-a-memory-database-in-multiple-threads" class="external-link" target="_blank">SQLAlchemy documentation about Using a Memory Database in Multiple Threads</a>
|
@ -0,0 +1,41 @@
|
||||
1. Import `pytest`.
|
||||
|
||||
2. Use the `@pytest.fixture()` decorator on top of the function to tell pytest that this is a **fixture** function (equivalent to a FastAPI dependency).
|
||||
|
||||
We also give it a name of `"session"`, this will be important in the testing function.
|
||||
|
||||
3. Create the fixture function. This is equivalent to a FastAPI dependency function.
|
||||
|
||||
In this fixture we create the custom **engine**, with the in-memory database, we create the tables, and we create the **session**.
|
||||
|
||||
Then we `yield` the `session` object.
|
||||
|
||||
4. The thing that we `return` or `yield` is what will be available to the test function, in this case, the `session` object.
|
||||
|
||||
Here we use `yield` so that **pytest** comes back to execute "the rest of the code" in this function once the testing function is done.
|
||||
|
||||
We don't have any more visible "rest of the code" after the `yield`, but we have the end of the `with` block that will close the **session**.
|
||||
|
||||
By using `yield`, pytest will:
|
||||
|
||||
* run the first part
|
||||
* create the **session** object
|
||||
* give it to the test function
|
||||
* run the test function
|
||||
* once the test function is done, it will continue here, right after the `yield`, and will correctly close the **session** object in the end of the `with` block.
|
||||
|
||||
5. Now, in the test function, to tell **pytest** that this test wants to get the fixture, instead of declaring something like in FastAPI with:
|
||||
|
||||
```Python
|
||||
session: Session = Depends(session_fixture)
|
||||
```
|
||||
|
||||
...the way we tell pytest what is the fixture that we want is by using the **exact same name** of the fixture.
|
||||
|
||||
In this case, we named it `session`, so the parameter has to be exactly named `session` for it to work.
|
||||
|
||||
We also add the type annotation `session: Session` so that we can get autocompletion and inline error checks in our editor.
|
||||
|
||||
6. Now in the dependency override function, we just return the same `session` object that came from outside it.
|
||||
|
||||
The `session` object comes from the parameter passed to the test function, and we just re-use it and return it here in the dependency override.
|
@ -0,0 +1,23 @@
|
||||
1. Create the new fixture named `"client"`.
|
||||
|
||||
2. This **client fixture**, in turn, also requires the **session fixture**.
|
||||
|
||||
3. Now we create the **dependency override** inside the client fixture.
|
||||
|
||||
4. Set the **dependency override** in the `app.dependency_overrides` dictionary.
|
||||
|
||||
5. Create the `TestClient` with the **FastAPI** `app`.
|
||||
|
||||
6. `yield` the `TestClient` instance.
|
||||
|
||||
By using `yield`, after the test function is done, pytest will come back to execute the rest of the code after `yield`.
|
||||
|
||||
7. This is the cleanup code, after `yield`, and after the test function is done.
|
||||
|
||||
Here we clear the dependency overrides (here it's only one) in the FastAPI `app`.
|
||||
|
||||
8. Now the test function requires the **client fixture**.
|
||||
|
||||
And inside the test function, the code is quite **simple**, we just use the `TestClient` to make requests to the API, check the data, and that's it.
|
||||
|
||||
The fixtures take care of all the **setup** and **cleanup** code.
|
Reference in New Issue
Block a user