8
\$\begingroup\$

For an e-commerce web application I have written a shopping cart, and some tests for it.

I have used a course on Lynda.com, on building an e-commerce website, as inspiration on how to build the shopping cart. So it shares a lot of code with the one being thought there.

The shopping cart is obviously not finished, but I want to start testing as much of it's functionality as possible while in the process of developing it.

So, I am curious how more experienced programmers judge this piece of code.

Shopping cart code

from django.conf import settings
from plant_data.models import Product
 def __init__(self, session):
 """
 Initialize shopping cart.
 """
 self.session = session
 cart = self.session.get(settings.CART_SESSION_ID)
 if not cart:
 cart = self.session[settings.CART_SESSION_ID] = {}
 self.cart = cart
 
 def add(self, product, quantity=1, update_quantity=False):
 """
 Add new product to cart, or update quantity of item already in cart.
 """
 product_id = str(product.id)
 if product_id not in self.cart:
 self.cart[product_id] = {'quantity': 0,
 'price': str(product.price),
 'name': product.name,
 }
 if update_quantity:
 self.cart[product_id]['quantity'] = quantity
 else:
 self.cart[product_id]['quantity'] += quantity
 self.save()
 
 def get_cart_list(self):
 """
 Returns the cart as a list, this format is more suitable than a dictionary for
 the frontend to make a detail view with.
 """
 cart_list = []
 print(self.cart)
 for product_id, value in self.cart.items():
 cart_list.append({
 "id": product_id,
 "name": value['name'],
 "price": value['price'],
 "quantity": value['quantity'],
 })
 return cart_list
 
 def save(self):
 self.session[settings.CART_SESSION_ID] = self.cart
 self.session.modified = True

The tests

from decimal import Decimal
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase, RequestFactory
from .cart import Cart
from plant_data.models import Product
class CartInitializeTestCase(TestCase):
 # Test cart initialization
 # Possible outcomes:
 # - cart.cart is an empty dictionary (with input: session that contains no cart)
 # - cart.cart contains is equal to the cart in the session
 def setUp(self):
 self.request = RequestFactory().get('/')
 # adding session
 middleware = SessionMiddleware()
 middleware.process_request(self.request)
 self.request.session.save()
 def test_initialize_cart_clean_session(self):
 """
 The cart is initialized with a session that contains no cart.
 In the end it should have a variable cart which is an empty dict.
 """
 request = self.request
 cart = Cart(request.session)
 self.assertEqual(cart.cart, {})
 def test_initialize_cart_filled_session(self):
 """
 The cart is initialized with a session that contains a cart.
 In the end it should have a variable cart which equal to the cart that
 is in the initial session.
 """
 existing_cart = {
 '1': {
 'name': 'first name',
 'price': '1.01',
 },
 '2': {
 'name': 'second name',
 'price': '34.1',
 }
 }
 request = self.request
 request.session['cart'] = existing_cart
 cart = Cart(request.session)
 self.assertEqual(cart.cart, existing_cart)
class CartAddTestCase(TestCase):
 # Test the add function
 # Possible outcomes:
 # Add to existing product:
 # - Add to an existing quantity
 # - Update an existing quantity
 # Add non existing product
 # - Add non existing product
 def setUp(self):
 self.request = RequestFactory().get('/')
 # adding session
 middleware = SessionMiddleware()
 middleware.process_request(self.request)
 self.request.session['cart'] = {
 '1': {
 'name': 'some name',
 'price': '1.01',
 'quantity': 8,
 },
 }
 self.request.session.save()
 self.existing_product = Product(
 id=1,
 name='some name',
 price=Decimal('1.01'),
 stock=8,
 )
 self.new_product = Product(
 id=2,
 name='other name',
 price=Decimal('2.23'),
 stock=12,
 )
 def test_cart_add_to_existing_quantity(self):
 """
 Test adding a quantity to an existing quantity.
 """
 cart = Cart(self.request.session)
 cart.add(product=self.existing_product,
 quantity=4,
 update_quantity=False,
 )
 new_cart = {
 '1': {
 'name': 'some name',
 'price': '1.01',
 'quantity': 12,
 },
 }
 self.assertEqual(cart.cart, new_cart)
 def test_cart_add_update_existing_quantity(self):
 """
 Test updating existing item quantity.
 """
 cart = Cart(self.request.session)
 cart.add(product=self.existing_product,
 quantity=4,
 update_quantity=True,
 )
 new_cart = {
 '1': {
 'name': 'some name',
 'price': '1.01',
 'quantity': 4,
 },
 }
 self.assertEqual(cart.cart, new_cart)
 def test_cart_add_new_product(self):
 cart = Cart(self.request.session)
 cart.add(product=self.new_product,
 quantity=4,
 update_quantity=False,
 )
 new_cart = {
 '1': {
 'name': 'some name',
 'price': '1.01',
 'quantity': 8,
 },
 '2': {
 'name': 'other name',
 'price': '2.23',
 'quantity': 4,
 }
 }
 self.assertEqual(cart.cart, new_cart)
class CartGetCartListTestCase(TestCase):
 # Test get_cart_list function
 # Possible outcome:
 # - returns a list of the cart that has been put in
 def test_get_cart_list(self):
 request = RequestFactory().get('/')
 # adding session
 middleware = SessionMiddleware()
 middleware.process_request(request)
 request.session['cart'] = {
 '1': {
 'name': 'some name',
 'price': '1.01',
 'quantity': 8,
 },
 '2': {
 'name': 'other name',
 'price': '2.23',
 'quantity': 4,
 }
 }
 request.session.save()
 test_cart_list = [
 {
 "id": "1",
 "name": "some name",
 "price": "1.01",
 "quantity": 8,
 },
 {
 "id": "2",
 "name": "other name",
 "price": "2.23",
 "quantity": 4,
 },
 ]
 cart = Cart(request.session)
 cart_list = cart.get_cart_list()
 self.assertEqual(cart_list, test_cart_list)
Mast
13.8k12 gold badges56 silver badges127 bronze badges
asked Jun 2, 2018 at 7:05
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The unit tests look good. Good job!


def __init__(self, session):
 """
 Initialize shopping cart.
 """
 self.session = session
 cart = self.session.get(settings.CART_SESSION_ID)
 if not cart:
 cart = self.session[settings.CART_SESSION_ID] = {}
 self.cart = cart

N.B. You are missing the class signature

class Cart:

Assuming the session is a dict like object, .get will implement a default keyword argument, which will simplify this code.

def __init__(self, session):
 """
 Initialize shopping cart.
 """
 self.session = session
 self.cart = self.session.get(settings.CART_SESSION_ID, {})

def add(self, product, quantity=1, update_quantity=False):
 """
 Add new product to cart, or update quantity of item already in cart.
 """
 product_id = str(product.id)
 if product_id not in self.cart:
 self.cart[product_id] = {'quantity': 0,
 'price': str(product.price),
 'name': product.name,
 }
 if update_quantity:
 self.cart[product_id]['quantity'] = quantity
 else:
 self.cart[product_id]['quantity'] += quantity
 self.save()

Storing the product price as a string ('price': str(product.price)) can behave weirdly you are converting from floating point numbers. Using decimal does make sense here, but may be overkill if you will only ever need 2 places of precision. It might be easier to work in cents/pennies.

I would move creating a new dictionary representation of a product. It may have non-trivial business logic in the future, and you may want to move it somewhere else. Both of these tasks will be easier when all the logic is encapsulated into a small function.

I think update_quantity would be better named as set_quantity. Update implies the new value depends on the previous value, whereas that is not the case here. If anything, I would have expected the statements inside if update_quantity: else: to be the other way around.

def product_dict(product):
 """"""
 return {
 'quantity': 0,
 'price': str(product.price),
 'name': product.name,
 }
def add(self, product, quantity=1, set_quantity=False):
 """
 Add a new product to cart, or set the quantity of item already in cart.
 """
 product_id = str(product.id)
 if product_id not in self.cart:
 self.cart[product_id] = product_dict(product)
 if set_quantity:
 self.cart[product_id]['quantity'] = quantity
 else:
 self.cart[product_id]['quantity'] += quantity
 self.save()
answered Aug 4, 2020 at 15:39
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.