🍓

FastAPI + Strawberry(Graphql)でFileUploadのtest作成

2021/11/14に公開

Intro

FastAPI + strawberryで簡単なgraphqlサーバーを作ってたらFileUploadのtest file作成に困ったので調べてみました。

strawberry公式文書(testing)

公式文書ではschemaだけを用いたtestの仕方は書いてあるけどfile uploadに関しては書いてなかった。

ただ、file_uploadの部分からgraphql-multipart-request-specという文書がリンクされていて読んでみたらgraphqlでファイルをアップロードするときはmultipart/form-dataの形式でrequestする必要があると書いてあってstrawberryもそれに従っていることだった。

스크린샷 2021-11-13 23.35.21

Graphql multipart/form-dataの形式

{
  "operations":"{query, variables}",
  "map":'{"file_0":["variables.file"]}',
  "file_o":FILE
}

FastAPI TEST

FastAPI - TESTING

公式文書によるとfastapiのTestClientrequestsと同じ使い方をしている。

Use the TestClient object the same way as you do with requests.

requestsモジュールでmultipart/form-dataをrequestするときはfilesargを使う。

TESTING

Schemaの作成

import strawberry
from strawberry.file_uploads import Upload
from typing import List

@strawberry.type
class UploadSuccess:
  result:str = "OK"
  filename:str

@strawberry.type
class Mutation:
    @strawberry.mutation
    async def test_single_upload(self, myfile: Upload) -> UploadSuccess:
      filename = myfile.filename
      return UploadSuccess(filename=filename)
    
    async def test_multiple_upload(self, myfiles: List[Upload]) -> List[UploadSuccess]:
      return [
        UploadSuccess(filename=myfile.filename)
        for myfile in myfiles
      ]
      
      

single fileの場合

from fastapi.testclient import TestClient
import json

client = TestClient(app) # appはgraphqlのrouteを含めているapp object

query = """
mutation MyMutation($file:Upload!){
	testSingleUpload(myfile:$file){
		result
		filename
	}
}
"""
variables = {'file':None}

def test_file_upload():
  file = open("testfile","rb")
  response = client.post('/graphql',
             files=dict(
               operations=(None, json.dumps({
               'query':query,
               'variables': variables
               })),
               file=file,
               map=(None, json.dumps({
                 'file': ['variables.file']
               }))
             )
             )
  assert response.status_code == 200

Multiple Fileの場合

from fastapi.testclient import TestClient
import json

client = TestClient(app) # appはgraphqlのrouteを含めているapp object

query = """
mutation MyMutation($file:[Upload!]!){
	testSingleUpload(myfiles:$files){
		result
		filename
	}
}
"""
variables = {'files': [None, None]}

def test_file_upload():
  file0 = open("testfile0","rb")
  file1 = open("testfile1", "rb")
  response = client.post('/graphql',
             files=dict(
               operations=(None, json.dumps({
               'query':query,
               'variables': variables
               })),
               file0=file0,
               file1=file1,
               map=(None, json.dumps({
                 'file0': ['variables.files.0'],
                 'file1': ['variables.files.1']
               }))
             )
             )
  assert response.status_code == 200

Discussion