Status code 400 (Bad Request) with Django API tests

66 views Asked by At

I'm working on a project with Django as framework and poetry as manager. Running some tests I found some problems with my viewsets, but I don't know exactly where it is. That's the error traceback:

poetry run python3 manage.py test tests/
Found 6 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F.F...
======================================================================
FAIL: test_create_order (test_orderviewset.TestOrderViewSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mateus/Área de Trabalho/EBAC/Back-end/BookStore/tests/test_orderviewset.py", line 47, in test_create_order
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 400 != 201

======================================================================
FAIL: test_create_product (test_productviewset.TestProductViewSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mateus/Área de Trabalho/EBAC/Back-end/BookStore/tests/test_productviewset.py", line 52, in test_create_product
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 400 != 201

----------------------------------------------------------------------
Ran 6 tests in 0.102s

FAILED (failures=2)
Destroying test database for alias 'default'...

My test file:

class TestOrderViewSet(APITestCase):
    client = APIClient()

    def setUp(self):
        self.category = CategoryFactory(title='comida')
        self.product = ProductFactory(title='CARNE', price=20.0, category=[self.category,])
        self.order = OrderFactory(product=[self.product,])

def test_create_order(self):
        product = ProductFactory()
        user = UserFactory()
        order_data = json.dumps({
            'products_id': [product.id,],
            'user': user.id
        })

        response = self.client.post(reverse('order-list', kwargs={'version': 'v1'}), data=order_data,
                                    content_type='application/json')

        import pdb

        pdb.set_trace()

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

I also made some factories using factory_boy to create some fake data to tests:

import factory

from django.contrib.auth.models import User
from .models import *


class UserFactory(factory.django.DjangoModelFactory):
    username = factory.Faker('pystr')
    email = factory.LazyAttribute(lambda x: '%[email protected]' % x.username)

    class Meta:
        model = User


class CategoryFactory(factory.django.DjangoModelFactory):
    title = factory.Faker('pystr')
    slug = factory.Faker('pystr')
    description = factory.Faker('pystr')
    active = factory.Iterator([True, False])

    class Meta:
        model = Category


class ProductFactory(factory.django.DjangoModelFactory):
    title = factory.Faker('pystr')
    price = factory.Faker('pyfloat')
    category = factory.LazyAttribute(CategoryFactory)

    @factory.post_generation
    def category(self, create, extracted, **kwargs):
        if not create:
            return
        if extracted:
            for category in extracted:
                self.category.add(category)

    class Meta:
        model = Product
        skip_postgeneration_save = True


class OrderFactory(factory.django.DjangoModelFactory):
    user = factory.SubFactory(UserFactory)

    @factory.post_generation
    def product(self, create, extracted, **kwargs):
        if not create:
            return
        if extracted:
            for product in extracted:
                self.product.add(product)

    class Meta:
        model = Order
        skip_postgeneration_save = True

And the serializers I created:

class ProductSerializer(serializers.ModelSerializer):
    category = CategorySerializer(required=True, many=True)
    category_id = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all(), write_only=True, many=True)


    class Meta:
        model = Product
        fields = ['title', 'description', 'price', 'active', 'category', 'category_id']
        extra_kwargs = {'category': {'required': False}}

    def create(self, validated_data):
        category_data = validated_data.pop('category_id')

        product = Product.objects.create(**validated_data)

        for category in category_data:
            product.category.add(category)
        return product

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = [
            'title',
            'slug',
            'description',
            'active',
        ]
        extra_kwargs = {'slug': {'required': False}}

class OrderSerializer(serializers.ModelSerializer):
    product = ProductSerializer(required=True, many=True)
    products_id = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all(), write_only=True, many=True)
    total = serializers.SerializerMethodField()

    # Model function method
    def get_total(self, instance):
        total = sum([product.price for product in instance.product.all()])
        return total

    def create(self, validated_data):

        products_data = validated_data.pop('products_id')
        user_data = validated_data.pop('user')

        order = Order.objects.create(user=user_data)

        for product in products_data:
            order.objects.add(product)
        return order

    class Meta:
        model = Order
        fields = ['product', 'total', 'user', 'products_id']
        extra_kwargs = {'product': {'required': True}}

Finally, my model's file:

from django.db import models
from django.contrib.auth.models import User


class Category(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    description = models.TextField(max_length=200, blank=True, null=True)
    active = models.BooleanField(default=True)

    def __str__(self):
        return self.title


class Product(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField(max_length=500, blank=True, null=True)
    price = models.FloatField(null=False)
    active = models.BooleanField(default=True)
    category = models.ManyToManyField(Category, blank=True)

    def __str__(self):
        return self.title


class Order(models.Model):
    product = models.ManyToManyField(Product, blank=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=False)

    def __str__(self):
        return self.customer.name

I try to add this field in a field 'extra_kwargs' (no success with that), the message persists. Then I checked if it was declared in it's respective fields. It was, but, somehow, my tests don't send that data using Factory, and post this in response content:

b'{"product":["This field is required."]}'
0

There are 0 answers