Skip to content

Source URL: https://stackoverflow.com/questions/76531474/django-ninja-testing

Django Ninja Testing

Ask a question

Asked 1 year, 1 month ago

Modified 4 months ago

Viewed 2k times

4

I am trying to create a test for an API I wrote using Django-Ninja.

Here is my model:

class Country(models.Model):
    created_at = models.DateTimeField(auto_created=True, auto_now_add=True)
    name = models.CharField(max_length=128, null=False, blank=False)
    code = models.CharField(max_length=128, null=False, blank=False, unique=True)
    timezone = models.CharField(max_length=128, null=False, blank=False)

Here is my schema:

class CountryAddSchema(Schema):
    name: str
    code: str
    timezone: str

Here is the endpoint of the post:

router.post("/add",
             description="Add a Country",
             summary="Add a Country", tags=["Address"],
             response={201: DefaultSchema, 401: DefaultSchema, 422: DefaultSchema, 500: DefaultSchema},
            url_name="address_country_add")
def country_add(request, country: CountryAddSchema):
    try:
        if not request.auth.belongs_to.is_staff:
            return 401, {"detail": "None Staff cannot add Country"}

        the_country = Country.objects.create(**country.dict())
        the_country.save()
        return 201, {"detail": "New Country created"}
    except Exception as e:
        return 500, {"detail": str(e)}

Finally, here is the test function:

def test_add_correct(self):
    """
    Add a country

    """
    data = {
        "name": "".join(choices(ascii_letters, k=32)),
        "code": "".join(choices(ascii_letters, k=32)),
        "timezone": "".join(choices(ascii_letters, k=32))
    }

    respond = self.client.post(reverse("api-1.0.0:address_country_add"), data, **self.AUTHORIZED_HEADER)
    self.assertEquals(respond.status_code, 201)
    self.assertDictEqual(json.loads(respond.content), {"detail": "New Country created"})

    the_country = Country.objects.last()
    self.assertDictEqual(
        data,
        {
            "name": the_country.name,
            "code": the_country.code,
            "timezone": the_country.timezone
        }
    )

Note that I set self.AUTHORIZED_HEADER in setUp.

And here is the error:

FAIL: test_add_correct (address.tests_country.CountryTest)
Add a country
----------------------------------------------------------------------
Traceback (most recent call last):
  File "SOME_PATH/tests_country.py", line 80, in test_add_correct
    self.assertEquals(respond.status_code, 201)
AssertionError: 400 != 201

I can add a country using the Swagger provided by Django-Ninja. I mean the endpoint works. But I cannot test it using djano.test.Client.

Any Idea?

Update:

Here is the curl code generated by swagger:

curl -X 'POST' \
  'http://127.0.0.1:8000/api/address/country/add' \
  -H 'accept: application/json' \
  -H 'X-API-Key: API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "string",
  "code": "string",
  "timezone": "string"
}'

3 Answers 3

Sort by: Reset to default

Highest score (default) Trending (recent votes count more) Date modified (newest first) Date created (oldest first)

4

I had an error {"detail": "Cannot parse request body"} .

It turns out that Django-ninja expects the data to be passed as JSON, but by default the test client sets content_type to multipart/form-data; boundary=BoUnDaRyStRiNg. When you explicitly specify that the content type should be JSON, it will work.

Client().post(url, {"your": "dict"}, content_type="application/json")

Note: Setting the content type in the headers will not work because it will be overridden.

Share

Improve this answer

Follow

Answered Nov 15, 2023 at 8:22

Image 3: Jorrick Sleijster's user avatar

Jorrick SleijsterJorrick Sleijster

109711 gold badges1212 silver badges2626 bronze badges

Add a comment |

2

Try converting it to JSON:

self.client.post(
   reverse("api-1.0.0:address_country_add"),
   json.dumps(data),
   content_type="application/json", 
   **self.AUTHORIZED_HEADER
)

Share

Improve this answer

Follow

Answered Jul 10, 2023 at 7:26

Image 4: Djangonaut's user avatar

DjangonautDjangonaut

5,77177 gold badges4141 silver badges5555 bronze badges

2

  • I have tried it. I found a solution. I used urllib.parse.urlencode. I will post the answer as soon as possible.

– MSH

Commented Jul 10, 2023 at 20:32

  • Hi, could you please post your answer? I am running into a similar issue

– Jorrick Sleijster

Commented Nov 15, 2023 at 8:12

Add a comment |

1

When sending data with the Django test client, make sure to send it as JSON data since your endpoint expects JSON:

def test_add_correct(self):
    """
    Add  country
    """
    data = {
        "name": "".join(choices(ascii_letters, k=32)),
        "code": "".join(choices(ascii_letters, k=32)),
        "timezone": "".join(choices(ascii_letters, k=32))
    }

    # Convert data to JSON format
    data_json = json.dumps(data)

    # Send the request with JSON data
    respond = self.client.post(reverse("api-1.0.0:address_country_add"), data=data_json, content_type='application/json', **self.AUTHORIZED_HEADER)

    # Rest of your test code_____


This should match your API endpoint's expectations and resolve the 400 status code issue.

Share

Improve this answer

Follow

Answered Mar 18 at 5:11

Image 5: Nayem Jaman Tusher's user avatar

Nayem Jaman Tusher

97122 gold badges88 silver badges2222 bronze badges

Add a comment |


本文总阅读量次