Fastapi: How to test against UploadFile parameter

Created on 8 Jun 2020  路  9Comments  路  Source: tiangolo/fastapi

First check

  • [ ] I added a very descriptive title to this issue.
  • [ ] I used the GitHub search to find a similar issue and didn't find it.
  • [ ] I searched the FastAPI documentation, with the integrated search.
  • [ ] I already searched in Google "How to X in FastAPI" and didn't find any information.
  • [ ] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [ ] I already checked if it is not related to FastAPI but to Pydantic.
  • [ ] I already checked if it is not related to FastAPI but to Swagger UI.
  • [ ] I already checked if it is not related to FastAPI but to ReDoc.
  • [ ] After submitting this, I commit to one of:

    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

    • I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

    • Implement a Pull Request for a confirmed bug.

Example

Here's a self-contained, minimal, reproducible, example with my use case:

from fastapi import FastAPI

router = FastAPI()


@router.post("/_config")
def create_index_config(upload_file: UploadFile = File(...)):

    config = settings.reads()
    created_config_file: Path = Path(config.config_dir, upload_file.filename)

    try:
        with created_config_file.open('wb') as write_file:
            shutil.copyfileobj(upload_file.file, write_file)

    except Exception as err:
        raise HTTPException(detail=f'{err} encountered while uploading {upload_file.filename}',
                            status_code=HTTPStatus.INTERNAL_SERVER_ERROR)
    finally:
        upload_file.file.close()

    return JSONResponse({"message": f'uploaded config {upload_file.filename}'})

Description

I'm just trying to test against an endpoint that uses uploadfile and I can't find how to send a json file to fastapi

def test_one():
  _test_upload_file = <path to file>
 with TestClient(app) as client:
        # TODO how to upload a file
        response = client.post('/_config',
                               data= <assuming something goes here ???> )
        assert response.status_code == HTTPStatus.CREATED

Environment

  • OS: [e.g. Linux / Windows / macOS]: OSX/docker
  • FastAPI Version [e.g. 0.3.0]: fastapi==0.54.1
  • Python version: Python 3.6.8

Additional context


data=_test_upload_file.open('rb') yields a 422 error
I have also tried this and get a 422 error

question

Most helpful comment

Thanks for the help here @Kludex ! :clap: :bow:

Thanks for reporting back and closing the issue @mwilson8 :+1:

All 9 comments

I've solved this yesterday on gitter. Could you search for "UploadFile" there? I'm busy now. I can post it here later if I remember.. or you can check there and post the solution here as well to help others.

Here you have: https://stackoverflow.com/questions/60783222/how-to-test-a-fastapi-api-endpoint-that-consumes-images

Example:

with open(fpath, "wb") as f:
    response = client.post("/", files={"file": ("filename", f, "image/jpeg")})

Here you have: https://stackoverflow.com/questions/60783222/how-to-test-a-fastapi-api-endpoint-that-consumes-images

Example:

with open(fpath, "wb") as f:
    response = client.post("/", files={"file": ("filename", f, "image/jpeg")})

I saw that but couldn't get it to work with my application

On gitter I found this

def test_upload(tmp_path):
    url = app.url_path_for("create_upload_files")
    with TestClient(app) as client:
        f = tmp_path / 'fileupload'
        with open(f, 'wb') as tmp:
            tmp.write(b'upload this')
        with open(f, 'rb') as tmp:
            files = {'files': tmp}
            response = client.post(url, files=files)
            assert response.status_code == 200

But that gives a 422 error when I try it in my code

    with TestClient(app) as client:
        # TODO how to upload a file
        with _test_upload_file.open('rb') as tmp:
            files = {'files': tmp}
            response = client.post('/_config',
                               files=files)
            assert response.status_code == HTTPStatus.CREATED

Big dumb on my part..

Working test:

_test_upload_file = Path('/usr/src/app/tests/files', 'new-index.json')
    _files = {'upload_file': _test_upload_file.open('rb')}
    with TestClient(app) as client:
        response = client.post('/_config',
                                files=_files)
        assert response.status_code == HTTPStatus.CREATED

    # remove the test file from the config directory
    _copied_file = Path('/usr/src/app/config', 'new-index.json')
    _copied_file.unlink()

I previously had the upload_file in the test named files -- FYSA that name must match the parameter of your endpoint, in my case it's upload_file so files wasn't working

Follow up with the code from Gitter also worked, thanks :)

Thanks for the help here @Kludex ! :clap: :bow:

Thanks for reporting back and closing the issue @mwilson8 :+1:

Here you have: https://stackoverflow.com/questions/60783222/how-to-test-a-fastapi-api-endpoint-that-consumes-images

Example:

with open(fpath, "wb") as f:
    response = client.post("/", files={"file": ("filename", f, "image/jpeg")})

This didn't work for me. I had to change the with open(fpath, "wb") as f to with open(fpath, "rb") as f:
Using read instead of write operation here works :)

A slight follow up, how do you test against the actual function as opposed to the endpoint?

Working test against the endpoint:

_test_upload_file = Path('filepath')
    _files = {'upload_file': _test_upload_file.open('rb')}
    with TestClient(app) as client:
        response = client.post('/_config',
                               files=_files,
                               )
    assert response.status_code == HTTPStatus.OK

But I also want to test with

response = service.function_within_endpoint(upload_file=<...what goes here...>)

Have you tried passing an instance of UploadFile?

Was this page helpful?
0 / 5 - 0 ratings