FAST API + Strawberry GraphQL PyTest Testing Error - python

I currently use Fast API GraphQL+ Strawberry with pytest to test graphql schema queries and mutations.
But when running multiple tests it sends me an error.
$ 'message': "'NoneType' object has no attribute 'send'",
My Python file for conf tests
import pytest
from src.graphql.db.session import get_session
from tests.graphql.db import overide_get_session
from fastapi.testclient import TestClient
from src.app import create_app
#pytest.fixture
def test_client_keep_alive():
app = create_app()
app.dependency_overrides[get_session] = overide_get_session
client = TestClient(app)
return client
My python file for tests
from tests.graphql.queries import get_users_query
class TestClass:
def test_one_get_users(self,test_client_keep_alive):
response = test_client_keep_alive.get(
"/graphql",
params = {
"query": get_users_query,
}
)
print(response.json())
def test_two_get_users(self,test_client_keep_alive):
response = test_client_keep_alive.get(
"/graphql",
params = {
"query": get_users_query,
}
)
print(response.json())

Something in the code you are trying to test, calls a send() method from a object that does not exist. Try adding your code to the example, to better understand what's happening.
But from the information available, I would say that you probably need to mock that missing object.

Related

FastAPI test client redirects the request when it is a POST or PUT

I'm writing test for a FastAPI application. When I write test for an endpoint with GET method everything works as expected, but when I call an endpoint with POST method somehow my request gets redirected to http://testserver this is an example of my endpoints:
from json import JSONDecodeError
from fastapi import APIRouter
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.status import HTTP_400_BAD_REQUEST
router = APIRouter()
#router.post("/test")
async def test(
request: Request,
):
try:
body = await request.json()
except JSONDecodeError:
return JSONResponse(content={}, status_code=HTTP_400_BAD_REQUEST)
return JSONResponse(content=body)
and this is an example of my test:
from starlette.testclient import TestClient
from app import app
client = TestClient(app)
def test_cookies():
res = client.post(
"api/test/",
json={
"name": "test"
},
)
assert 200 == res.status_code
again this happens just with POST and PUT requests the GET request works just fine. any idea why is this happening?
Your endpoint is registered as /api/test, while you're calling /api/test/ - notice the difference in the trailing slash.
By default FastAPI will issue a redirect to make your browser talk to the correct endpoint. The http://testserver URL you're seeing is the internal hostname used in the TestClient.

how do I make API request after deploying uvicorn via fastapi in mylaptop

I am new to FastAPI/model deployment, I managed to created a model which can return a result using the FastAPI docs path in my local server/laptop.
My question is, how can i access this model in jupyter notebook?
Trying to relate into other API request such as Foursquare API. How do I wrap a params dictionary
Tried the following however it is not working
import requests
url = 'http://127.0.0.1:8000/predict'
params = { 'title' : ['Product Operations Software Engineer (DevOps / SRE)']}
result = requests.post(url, params)
<Response [422]>
result.text
'{"detail":[{"loc":["body"],"msg":"value is not a valid dict","type":"type_error.dict"}]}'
Input
Output
My Code
from fastapi import FastAPI
import numpy as np
import dill
import pandas as pd
import uvicorn
import re
from pydantic import BaseModel
app = FastAPI()
class JobTitle(BaseModel):
title: list
#load model
with open("logisticmodel", "rb") as to_load:
logistic_model = dill.load(to_load)
#app.get("/")
def home():
return 'welcome'
#app.post("/predict")
def predict(request: JobTitle):
X = pd.Series(request.title)
result = logistic_model.predict(X)
return {'Type' : list(result) }
Your code here indicates that your data should be put inside the HTTP request body
#app.post("/predict")
def predict(request: JobTitle):
...
But when you issue the HTTP request with the requests module,
you didn't put anything to your request body.
You should use json argument so the data can be put to the right place:
result = requests.post(
url,
json={'title': ['Product Operations Software Engineer (DevOps / SRE)']}
)
You can check this for sending an HTTP request with request body using requests module,
and this for building an API with a request body.
You need to use .dict() to get the dictionary of BaseModel instance. In your case, request.dict()
Also, Define your model like this,
class JobTitle(BaseModel):
title: List[str]
This works:
>>> result = requests.post(url, json=params)
>>> result
<Response [200]>

flask python creating swagger document error

I am trying to generate a swagger documentation for the API I am building in flask however when I execute below code I get very staring error, What I am missing here ?
This is what I have tried :
try:
from flask import Flask, g, request
from flask_restful import Resource, Api
from flask_limiter.util import get_remote_address
from flask_limiter import Limiter
from flasgger import Swagger
from flasgger.utils import swag_from
from flask_restful_swagger import swagger
from flask_httpauth import HTTPTokenAuth
import os
import json
except Exception as e:
print("some modules are missing {}".format(e))
app = Flask(__name__)
api = Api(app)
limiter = Limiter(app, key_func=get_remote_address)
limiter.init_app(app)
api = swagger.docs(Api(app), apiVersion='0.1', api_spec_url='/doc')
auth = HTTPTokenAuth(scheme='Token')
tokens = {
'awserftggyjkk34)ghtyrjrhhye34nnmw': 'cadmin',
'bwsosjhee(dhj345gtyuioplsertbsjkl': 'dadmin',
'mnchthas(sjklertyusvfgmbshdls234h': 'eadmin'
}
#auth.verify_token
def verify_token(token):
if token in tokens:
g.current_user = tokens[token]
return True
return False
class defr(Resource):
decorators = [limiter.limit("100/day")]
#swagger.model
#swagger.operation(notes='my notes ntes')
#auth.login_required
def currentuser(self):
return "Hello, %s!" % g.current_user
api.add_resource(defr, '/user')
error :
File "C:\Users\codamd\AppData\Local\Continuum\anaconda3\lib\site-packages\flask_restful_swagger\swagger.py", line 294, in extract_operations
for method in [m.lower() for m in resource.methods]:
TypeError: 'NoneType' object is not iterable
Any help would be great
The problem here is that you've used a custom named method in your defr class. The Flask-RESTful documentation specifies defining HTTP methods in your Resource class e.g.
class defr(Resource):
decorators = [limiter.limit("100/day")]
#swagger.model
#swagger.operation(notes='my notes ntes')
#auth.login_required
def get(self):
return "Hello, %s!" % g.current_user

How to test functions that request data from external endpoints in django

I am trying to test my functions on my django api that perform external requests to external api. How can
i test the following scenarios: success, failed, and exceptions like timeout
The following is a simplified functionality
def get_quote(*args):
# log request
try:
response = requests.post(url, json=data)
# parse this response
except:
# log file :)
finally:
# log_response(...)
return parsed_response or None
None: response can be success, failed, can timeout. I want to test those kind of scenarios
You can mock the result of calling the external API and set an expected return value in the test function:
from unittest.mock import patch
from django.test import TestCase
class ExternalAPITests(TestCase):
#patch("requests.post")
def test_get_quote(self, mock):
mock.return_value = "predetermined external result"
self.assertEquals("expected return value", get_quote())
You can use the responses package - https://pypi.org/project/responses/
import unittest
import responses
from your_package import get_quote
class TestPackage(unittest.TestCase):
#responses.activate
def test_get_quote(self):
url = "http://some_fake_url.com"
responses.add(responses.POST, url, json={"test": "ok"}, status=200)
self.assertDictEqual({"test": "ok"}, get_quote(url))
#responses.activate
def test_get_quote_with_exception(self):
url = "http://some_fake_url.com"
responses.add(responses.POST, url, body=Exception('...'))
with self.assertRaises(Exception):
get_quote(url)

OpenTracing Python requests: Propagate span

I have a Python Flask application that uses the requests library. I am trying to implement OpenTracing in the application. A simplified version is:
from flask_opentracing import FlaskTracer
from flask import Flask
from jaeger_client import Config
from opentracing_instrumentation.client_hooks import install_all_patches
from os import getenv
import requests
JAEGER_HOST = getenv('JAEGER_HOST', 'localhost')
def initialise_tracer():
config = Config(
config={
'local_agent': {
'reporting_host': 'jaeger'
}
},
service_name='opentracing_test',
validate=True
)
return config.initialize_tracer()
app = Flask(__name__)
tracer = FlaskTracer(initialise_tracer, trace_all_requests=True, app=app)
install_all_patches()
#app.route('/foo')
def foo():
r = requests.get('http://localhost/bar')
return 'foo : ' + r.text
#app.route('/bar')
def bar():
return 'bar'
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=80)
All endpoints are traced because of giving trace_all_requests to FlaskTracer. All requests made with the requests library are traced because of install_all_patches() from opentracing_instrumentation.
My '/foo' endpoint makes a request to '/bar'. When I access the '/foo' endpoint and view the traces, I can see two separate traces: One containing just '/foo', and a second that contains both the requests GET and the call to '/bar'. I would have expected the spans to propagate through the entire process and therefore I should see a single trace with '/foo' -> GET -> '/bar'.
Do I need to manually set the headers on each of my requests to pass the span? It looks like that is exactly what the requests monkey patch should do...

Categories