I'm writing a test to validate the uniqueness of a field. In this test, I'm trying to validate that a Product's slug is unique. My initial test was the following:
from django.test import TestCase
from django.urls import reverse
from django.forms import ValidationError
from django.db.utils import IntegrityError
from rest_framework.test import APIClient
from rest_framework import status
from .factories import ProductFactory
from .models import Product
from .serializer import ProductSerializer
PRODUCTS_URL = reverse('product-list')
def create_product(**params):
""" Helper function to create a new product """
return Product.objects.create(**params)
class PrivateProductsTests(TestCase):
def setUp(self):
self.client = APIClient()
def test_an_error_occurs_when_forcing_the_same_slug_for_different_products(self):
product1 = ProductFactory.create(slug='')
serializer1 = ProductSerializer(product1)
product1_data = serializer1.data
product1 = create_product(**product1_data)
product2 = ProductFactory.create(slug=product1.slug)
serializer2 = ProductSerializer(product2)
product2_data = serializer2.data
self.assertRaises(IntegrityError, create_product(**product2_data))
But it was failing with the following error:
django.db.utils.IntegrityError: duplicate key value violates unique constraint "products_product_slug_key" DETAIL: Key (slug)=(james-maxwell) already exists.
looking around I found the following question which provided an answer:
Fail on Testing IntegrityError UNIQUE constraint
now my test looks like:
def test_an_error_occurs_when_forcing_the_same_slug_for_different_products(self):
product1 = ProductFactory.create(slug='')
serializer1 = ProductSerializer(product1)
product1_data = serializer1.data
product1 = create_product(**product1_data)
product2 = ProductFactory.create(slug=product1.slug)
serializer2 = ProductSerializer(product2)
product2_data = serializer2.data
# there are two kinds of IntegrityError classes at play here, one
# that's imported from the test and one raised by the db
with self.assertRaises(IntegrityError) as raised:
product2 = create_product(**product2_data)
# print(type(raised.exception))
self.assertEqual(IntegrityError, type(raised.exception))
but I wanted to go one step further an check that nothing had been stored in the system, so I modified my test to be (check the last 2 lines):
def test_an_error_occurs_when_forcing_the_same_slug_for_different_products(self):
product1 = ProductFactory.create(slug='')
serializer1 = ProductSerializer(product1)
product1_data = serializer1.data
product1 = create_product(**product1_data)
product2 = ProductFactory.create(slug=product1.slug)
serializer2 = ProductSerializer(product2)
product2_data = serializer2.data
print(product1.slug)
print(product2_data)
# self.assertRaises(IntegrityError, create_product(**product2_data))
# there are two kinds of IntegrityError classes at play here, one
# that's imported from the test and one raised by the db
with self.assertRaises(IntegrityError) as raised:
product2 = create_product(**product2_data)
# print(type(raised.exception))
self.assertEqual(IntegrityError, type(raised.exception))
# verify there's only one item in the db
res = self.client.get(PRODUCTS_URL)
print(res.data)
After adding those two lines I'm getting the following error:
"An error occurred in the current transaction. You can't " django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
How can I address this issue?