114

I have code within a Flask application that uses JSONs in the request, and I can get the JSON object like so:

Request = request.get_json()

This has been working fine, however I am trying to create unit tests using Python's unittest module and I'm having difficulty finding a way to send a JSON with the request.

response=self.app.post('/test_function', 
                       data=json.dumps(dict(foo = 'bar')))

This gives me:

>>> request.get_data()
'{"foo": "bar"}'
>>> request.get_json()
None

Flask seems to have a JSON argument where you can set json=dict(foo='bar') within the post request, but I don't know how to do that with the unittest module.

dspencer
  • 4,297
  • 4
  • 22
  • 43
Sepehr Nazari
  • 3,744
  • 4
  • 12
  • 17
  • What does the `request.data` contains? Often when json parsing fails due to wrong input, it fails silently and return `None` thus the raw input data may not be json. – Benoît Latinier Mar 03 '15 at 16:56
  • >>> request.get_data() '{"foo": "bar"}' >>> request.get_json() None I'm not quite sure how flask's request works, but it seems to separate data and json, and I can't figure out how to send information to the json rather than the data, if that makes any sense. – Sepehr Nazari Mar 03 '15 at 17:19
  • 9
    i think it's the content type headers, try to set them to appliacation/json. also the force parameter is helpful but you probably don't want to go there just to make unittests pass, better to change mime – user3012759 Mar 03 '15 at 17:28

2 Answers2

220

Changing the post to

response=self.app.post('/test_function', 
                       data=json.dumps(dict(foo='bar')),
                       content_type='application/json')

fixed it.

Thanks to user3012759.

vidstige
  • 12,492
  • 9
  • 66
  • 110
Sepehr Nazari
  • 3,744
  • 4
  • 12
  • 17
  • was wrecking my head around with this. Don't understand why you have to dump the data when you're already specifying the `application/json` content type. – dimmg Nov 19 '16 at 16:12
  • I believe it's because everything you send in a post has to be a string. – Sepehr Nazari Nov 30 '16 at 17:57
  • 23
    Amazing this isn't in the docs, because flask's test_client has no API docs! – rjurney Mar 09 '18 at 01:32
  • 1
    How to get data from response? – variable Nov 13 '19 at 13:23
  • 2
    @variable, with a response from a `post` request like `resp = client.post('/my/endpoint/',json=my_json_data)` you can access the data as bytes with `resp.data`. – amiabl Jun 17 '20 at 10:01
  • be careful: using a call with the `json` instead of `data` argument (for example) = {"i": 3, "j": 5, "dx": 7, "m": 8} you risk of mixing keys. Order is sometimes important. – MrNinjamannn Dec 22 '21 at 17:29
58

Since Flask 1.0 release flask.testing.FlaskClient methods accepts json argument and Response.get_json method added, see pull request

    with app.test_client() as c:
        rv = c.post('/api/auth', json={
            'username': 'flask', 'password': 'secret'
        })
        json_data = rv.get_json()

For Flask 0.x compatibility you may use receipt below:

    from flask import Flask, Response as BaseResponse, json
    from flask.testing import FlaskClient
    
    
    class Response(BaseResponse):
        def get_json(self):
            return json.loads(self.data)
    
    
    class TestClient(FlaskClient):
        def open(self, *args, **kwargs):
            if 'json' in kwargs:
                kwargs['data'] = json.dumps(kwargs.pop('json'))
                kwargs['content_type'] = 'application/json'
            return super(TestClient, self).open(*args, **kwargs)
    

    app = Flask(__name__)
    app.response_class = Response
    app.test_client_class = TestClient
    app.testing = True
Victor Gavro
  • 1,347
  • 9
  • 13