The code I wrote runs well without unit test. But fails when I unit test it.
Here is the source code
"""
shoppingcart.py
This program allows me to manage my shopping cart items
#autohor chuzksy
#version 2017-11-30
"""
class ShoppingCart(object):
"""This class manages shopping cart items in terms of
adding items, removing items and updating the cart list
"""
def __init__(self):
"""This is a constructor method belonging to the object of this ShoppingCart class"""
self.total = 0
self.items = {}
def add_item(self, item_name, quantity, price):
"""This method add items to the shopping cart dictionary"""
self.total = self.total + (quantity * price)
self.items.update({item_name: quantity})
def remove_item(self, item_name, quantity, price):
"""This method removes an item from the shopping cart and updates it"""
for name, qty in self.items.items():
if item_name == name:
if quantity < qty:
del self.items[item_name]
self.items.update({item_name: qty - quantity})
self.total -= (quantity * price)
else:
del self.items[item_name]
break
def checkout(self, cash_paid):
"""This method allows the user to pay for the items in the shopping cart"""
if cash_paid < self.total:
return "Cash paid not enough"
else:
return cash_paid - self.total
class Shop(ShoppingCart):
"""This is another class inheriting attributes and methods from the ShoppingCart class"""
def __init__(self):
super().__init__(self)
self.quantity = 100
def remove_item(self):
self.quantity -= 1
Here is the Unit test code:
import unittest
from shoppingcart import ShoppingCart
from shoppingcart import Shop
class ShoppingCartTestCases(unittest.TestCase):
def setUp(self):
self.cart = ShoppingCart()
self.shop = Shop()
def test_cart_property_initialization(self):
self.assertEqual(self.cart.total, 0, msg='Initial value of total not correct')
self.assertIsInstance(self.cart.items, dict, msg='Items is not a dictionary')
def test_add_item(self):
self.cart.add_item('Mango', 3, 10)
self.assertEqual(self.cart.total, 30, msg='Cart total not correct after adding items')
self.assertEqual(self.cart.items['Mango'], 3, msg='Quantity of items not correct after adding item')
def test_remove_item(self):
self.cart.add_item('Mango', 3, 10)
self.cart.remove_item('Mango', 2, 10)
self.assertEqual(self.cart.total, 10, msg='Cart total not correct after removing item')
self.assertEqual(self.cart.items['Mango'], 1, msg='Quantity of items not correct after removing item')
def test_checkout_returns_correct_balance(self):
self.cart.add_item('Mango', 3, 10)
self.cart.add_item('Orange', 16, 10)
self.assertEqual(self.cart.checkout(265), 75, msg='Balance of checkout not correct')
self.assertEqual(self.cart.checkout(25), 'Cash paid not enough', msg='Balance of checkout not correct')
def test_shop_is_instance_of_shopping_cart(self):
self.assertTrue(isinstance(self.shop, ShoppingCart), msg='Shop is not a subclass of ShoppingCart')
def test_shop_remove_item_method(self):
for i in range(15):
self.shop.remove_item()
self.assertEqual(self.shop.quantity, 85)
if __name__ == '__main__':
unittest.main(exit = False)
print("test pass")
Here is the output I get after running the unit test program
Thank you so much in advance.
This should help solve the problem with your subclass:
class Shop(ShoppingCart):
def __init__(self):
self.quantity = 100
def remove_item(self):
self.quantity = self.quantity - 1
return self.quantity
The error is correct. The ShoppingCartTestCases has not atttribute called "shop" unless you call the setUp method first. Are you sure that's being done?
Related
I am attempting to learn OOP in Python so I wanted to ask why do we pass parameters into the method when calling it, while the object is passed automatically by Python into the method as the first parameter and can be used to automatically identify and call its attributes instead of passing them when calling the method?
Why don't we do this:
class Item:
def calculate_total(self):
total = self.quantity * self.price
return total
item = Item()
item.price = 100
item.quantity = 5
item.calculate_total()
instead of this:
class Item:
def calculate_total(self, x, y):
total = x * y
return total
item = Item()
item.price = 100
item.quantity = 5
item.calculate_total(item.price, item.quantity)
The question is kinda flawed, as none of those should be seen in real-life code.
Attributes should be declared inside the class, preferrably at the moment of object initialisation.
class Item:
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
def calculate_total(self):
total = self.quantity * self.price
return total
item = Item(100, 5)
item.calculate_total()
This way we don't have a risk of self.price and self.quantity not being defined when we call calculate_total.
But, even with what you provided, why 2nd method would be worse quickly becomes apparent if you try to calculate things multiple times. Let's say you've got 3 slightly different totals (different currency maybe).
Would you rather write
item.calculate_total1(item.price, item.quantity)
item.calculate_total2(item.price, item.quantity)
item.calculate_total3(item.price, item.quantity)
or
item.calculate_total1()
item.calculate_total2()
item.calculate_total2()
?
As #Pranav Hosangadi mentions, if there are any parameters that do not have a place in the attributes of a class (e.g. discount, which can vary for different sales of the same item), that is where we would pass them to the method:
class Item:
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
def calculate_total(self, discount):
total = self.quantity * self.price * (1 - discount)
return total
item = Item(100, 5)
discount = 0.15
print(item.calculate_total(discount))
The Product class seems to work fine but I'm trying to figure out how to get the Inventory class to separate each product into there specific categories. I feel like I'm close but whenever I try and print out the inventory it just shows where it's stored in memory and doesn't actually print anything out. The output i receive when running is at the bottom. I want it to print out the actual products and data, not the instance of it stored in memory.
class Product:
def __init__(self, pid, price, quantity):
self.pid = pid
self.price = price
self.quantity = quantity
def __str__(self):
#Return the strinf representing the product
return "Product ID: {}\t Price: {}\t Quantity: {}\n".format(self.pid, self.price, self.quantity)
def get_id(self):
#returns id
return self.pid
def get_price(self):
#returns price
return self.price
def get_quantity(self):
#returns quantity
return self.quantity
def increase_quantity(self):
self.quantity += 1
def decrease_quantity(self):
self.quantity -= 1
def get_value(self):
value = self.quantity * self.price
return 'value is {}'.format(value)
product_1 = Product('fishing', 20, 10)
product_2 = Product('apparel', 35, 20)
class Inventory:
def __init__(self, products):
self.products = products
self.fishing_list = []
self.apparel_list = []
self.value = 0
def __repr__(self):
return "Inventory(products: {}, fishing_list: {}, apparel_list: {}, value: {})".format(self.products, self.fishing_list, self.apparel_list, self.value)
def add_fishing(self):
for product in self.products:
if product.get_id() == 'fishing':
self.fishing_list.append(product)
return '{} is in the fishing section'.format(self.fishing_list)
def add_apparel(self):
for product in self.products:
if product.get_id() == 'apparel':
self.apparel_list.append(product)
return '{} is in the apparel section'.format(self.apparel_list)
inventory_1 = Inventory([product_1, product_2])
inventory_1.add_fishing()
print(inventory_1)
OUTPUT = Inventory(products: [<main.Product instance at 0x10dbc8248>, <main.Product instance at 0x10dbc8290>], fishing_list: [<main.Product instance at 0x10dbc8248>], apparel_list: [], value: 0)
You need to specify how an object of the class Inventory should be printed.
To do this you need to implement at least one of the following functions in your class.
__repr__
__str__
This answer helps, which of both you should use: https://stackoverflow.com/a/2626364/8411228
An implementation could look something like this:
class Inventory:
# your code ...
def __repr__(self):
return str(self.products) + str(self.fishing_list) + str(self.apparel_list) + str(self.value)
# or even better with formatting
def __repr__(self):
return f"Inventory(products: {self.products}, fishing_list: {self.fishing_list}, apparel_list: {self.apparel_list}, value: {self.value})
Note that I used in the second example f strings, to format the output string.
I am taking a test in OOP, where I am expected to create a ShoppingCart.
I have written my code which passes all the test from unittest, but when I try to submit, I get this error/bug
/bin/sh: 1: python/nose2/bin/nose2: not found
Below I have shown my code and the unittest.
Unittiest
import unittest
class ShoppingCartTestCases(unittest.TestCase):
def setUp(self):
self.cart = ShoppingCart()
self.shop = Shop()
def test_cart_property_initialization(self):
self.assertEqual(self.cart.total, 0, msg='Initial value of total not correct')
self.assertIsInstance(self.cart.items, dict, msg='Items is not a dictionary')
def test_add_item(self):
self.cart.add_item('Mango', 3, 10)
self.assertEqual(self.cart.total, 30, msg='Cart total not correct after adding items')
self.assertEqual(self.cart.items['Mango'], 3, msg='Quantity of items not correct after adding item')
def test_remove_item(self):
self.cart.add_item('Mango', 3, 10)
self.cart.remove_item('Mango', 2, 10)
self.assertEqual(self.cart.total, 10, msg='Cart total not correct after removing item')
self.assertEqual(self.cart.items['Mango'], 1, msg='Quantity of items not correct after removing item')
def test_checkout_returns_correct_balance(self):
self.cart.add_item('Mango', 3, 10)
self.cart.add_item('Orange', 16, 10)
self.assertEqual(self.cart.checkout(265), 75, msg='Balance of checkout not correct')
self.assertEqual(self.cart.checkout(25), 'Cash paid not enough', msg='Balance of checkout not correct')
def test_shop_is_instance_of_shopping_cart(self):
self.assertTrue(isinstance(self.shop, ShoppingCart), msg='Shop is not a subclass of ShoppingCart')
def test_shop_remove_item_method(self):
for i in range(15):
self.shop.remove_item()
self.assertEqual(self.shop.quantity, 85)
myCOde
class ShoppingCart(object):
def __init__(self):
self.total = 0
self.items = {}
def add_item(self, item_name, quantity, price):
if item_name and quantity >= 1:
self.items.update({item_name : quantity})
if quantity and price >= 1:
self.total += (quantity * price)
def remove_item(self, item_name, quantity, price):
self.total -= (quantity * price)
if quantity >= self.items[item_name]:
try:
del self.items[item_name]
except (KeyError, ValueError):
return None
self.items[item_name] -= quantity
def checkout(self, cash_paid):
balance = 0
if cash_paid < self.total:
return "Cash paid not enough"
balance = cash_paid - self.total
return balance
class Shop(ShoppingCart):
def __init__(self):
ShoppingCart.__init__(self)
self.quantity = 100
def remove_item(self):
self.quantity -= 1
The root cause of the issue lies in the setUp() method in the unittest class. So running the unittest class on a python development environment such as IDLE exposes the issue further.
The full error actually is something like this
" Desktop/practice python/shoppingcarttestcases.py", line 4, in setUp
self.cart = ShoppingCart()
NameError: global name 'ShoppingCart' is not defined"
The solution may lie around properly defining/inheriting/implementing the class Shopping Cart.
I hope this narrows it down.
If you find this helpful and you get a solution please post on the forum as well.
HST2: OBJECT ORIENTED PROGRAMMING LAB
Create a class called ShoppingCart.
Create a constructor that takes no arguments and sets the total attribute to zero, and initializes an empty dict attribute named items.
Create a method add_item that requires item_name, quantity and price arguments. This method should add the cost of the added items to the current value of total. It should also add an entry to the items dict such that the key is the item_name and the value is the quantity of the item.
Create a method remove_item that requires similar arguments as add_item. It should remove items that have been added to the shopping cart and are not required. This method should deduct the cost of the removed items from the current total and also update the items dict accordingly.
If the quantity of an item to be removed exceeds the current quantity of that item in the cart, assume that all entries of that item are to be removed.
Create a method checkout that takes in cash_paid and returns the value of balance from the payment. If cash_paid is not enough to cover the total, return "Cash paid not enough".
Create a class called Shop that has a constructor which takes no arguments and initializes an attribute called quantity at 100.
Make sure Shop inherits from ShoppingCart.
In the Shop class, override the remove_item method, such that calling Shop's remove_item with no arguments decrements quantity by one.
# OOP Lab
class ShoppingCart(object):
def __init__(self):
total = 0
item = {}
self.total = total
self.item = item
def add_item(item_name, quantity, price):
cost = quantity * price
self.total += cost
self.item = {"item_name":quantity}
def remove_item(item_name,quantity,price):
cost = quantity * cost
self.total -= cost
for i in self.item:
if quantity > self.item[i]:
del self.item["item_name"]
def checkout(cash_paid):
if cash_paid < self.total:
return "Cash paid not enough"
class Shop(ShoppingCart):
def __init__(self):
quantity = 100
self.quantity = quantity
def remove_item():
self.quantity -= 1
#! Error State the following:
my add_item is having four argument instead of three each time i run this code:
Please i need help with this code, am new with python, i will appreciate any programming angel in python to rescue me now.
Try this, it should work out just fine:
class ShoppingCart(object):
def __init__(self):
self.total = 0
self.items = {}
def add_item(self, item_name, quantity, price):
self.total += quantity * price
if type(item_name) == str and quantity > 0:
self.items.update({item_name: quantity})
def remove_item(self, item_name, quantity, price):
if quantity >= self.items[item_name] and quantity >= 1:
items_cost = price * self.items[item_name]
self.total -= items_cost
del self.items[item_name]
else:
self.total -= quantity * price
self.items[item_name] -= quantity
def checkout(self, cash_paid):
balance = 0
if cash_paid < self.total:
return "Cash paid not enough"
balance = cash_paid - self.total
return balance
class Shop(ShoppingCart):
def __init__(self):
self.quantity = 100
def remove_item(self):
self.quantity -= 1
Class methods should accept self as the first argument so for example
def add_item(self, item_name, quantity, price):
Instead of
def add_item(item_name, quantity, price):
The "4th argument" being passed is implicitly self, that is why the number of arguments is one higher than you are expecting.
I'm writing a function that takes inputs in the form of code and returns a string.
class Item(object):
def __init__(self, code, name, stock, price):
self.code = code
self.name = name
self.stock = stock
self.price = price
beer = Item(124, "beer", 200, 12.90)
print(beer.code)
Is there any way to get the name "beer" from its code, 124. Like you could with a dictionary? dict = {124 : "beer"}
Assuming that first you had created a list of items, something like
items = []
items.append(Item(124, "beer", 200, 12.90)
items.append(Item(125, "diapers", 100, 5.90)
Then you could use a list comprehension to find items with a given code, e.g.
beer = [item for item in items if item.code==124][0]
This assumes you can guarantee there is only one item with code 124. You could even wrap this up in a function:
def find_item(code):
return [item for item in items if item.code==124][0]
You could create an Inventory class and do this:
class Item(object):
def __init__(self, code, name, stock, price):
self.code = code
self.name = name
self.stock = stock
self.price = price
def __repr__(self):
return '{}: {} - {} - {}'.format(self.code, self.name, self.stock, self.price)
class Inventory(object):
_inventory = {}
def find(self, code):
return self._inventory.get(code)
def add_item(self, code, name, stock, price):
if code in self._inventory:
raise KeyError('item with this code already present')
self._inventory[code] = Item(code, name, stock, price)
inventory = Inventory()
inventory.add_item(124, "beer", 200, 12.90)
inventory.add_item(125, "another beer", 400, 8.10)
search = inventory.find(124)
print search
print search.name
output:
124: beer - 200 - 12.9
beer