Permalink
Browse files

MVP

Still needs some tests.
  • Loading branch information...
0 parents commit d8915790af9c18123494ae65fcc71e812115562d @codeinthehole codeinthehole committed Nov 15, 2012
Showing with 299 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +27 −0 LICENSE
  3. +6 −0 README.rst
  4. +2 −0 makefile
  5. 0 oscar_testsupport/__init__.py
  6. +24 −0 oscar_testsupport/decorators.py
  7. +127 −0 oscar_testsupport/factories.py
  8. +89 −0 oscar_testsupport/testcases.py
  9. +23 −0 setup.py
@@ -0,0 +1 @@
+*.pyc
27 LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2011, Tangent Communications PLC and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of Tangent Communications PLC nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,6 @@
+===========================
+Testing utilities for Oscar
+===========================
+
+A simple package to allow the test utils from Oscar to be used by other
+packages.
@@ -0,0 +1,2 @@
+install:
+ python setup.py develop
No changes.
@@ -0,0 +1,24 @@
+def dataProvider(fn_data_provider):
+ """
+ Data provider decorator, allows another callable to provide the data for
+ the test. This is a nice feature from PHPUnit which is very useful. Am
+ sticking with the JUnit style naming as unittest does this already.
+
+ Implementation based on:
+ http://melp.nl/2011/02/phpunit-style-dataprovider-in-python-unit-test/#more-525
+ """
+ def test_decorator(test_method):
+ def execute_test_method_with_each_data_set(self):
+ for data in fn_data_provider():
+ if (len(data) == 2 and isinstance(data[0], tuple) and
+ isinstance(data[1], dict)):
+ # Both args and kwargs being provided
+ args, kwargs = data[:]
+ else:
+ args, kwargs = data, {}
+ try:
+ test_method(self, *args, **kwargs)
+ except AssertionError, e:
+ self.fail("%s (Provided data: %s, %s)" % (e, args, kwargs))
+ return execute_test_method_with_each_data_set
+ return test_decorator
@@ -0,0 +1,127 @@
+from decimal import Decimal as D
+import random
+import datetime
+
+from oscar.core.loading import get_class, get_classes
+
+Basket = get_class('basket.models', 'Basket')
+Free = get_class('shipping.methods', 'Free')
+Voucher = get_class('voucher.models', 'Voucher')
+OrderCreator = get_class('order.utils', 'OrderCreator')
+OrderTotalCalculator = get_class('checkout.calculators',
+ 'OrderTotalCalculator')
+Partner, StockRecord = get_classes('partner.models', ('Partner',
+ 'StockRecord'))
+(ProductClass,
+ Product,
+ ProductAttribute,
+ ProductAttributeValue) = get_classes('catalogue.models',
+ ('ProductClass',
+ 'Product',
+ 'ProductAttribute',
+ 'ProductAttributeValue'))
+(Range,
+ ConditionalOffer,
+ Condition,
+ Benefit) = get_classes('offer.models', ('Range', 'ConditionalOffer',
+ 'Condition', 'Benefit'))
+
+
+def create_product(price=None, title="Dummy title",
+ product_class="Dummy item class",
+ partner="Dummy partner", partner_sku=None, upc=None, num_in_stock=10,
+ attributes=None, **kwargs):
+ """
+ Helper method for creating products that are used in tests.
+ """
+ ic, __ = ProductClass._default_manager.get_or_create(name=product_class)
+ item = Product._default_manager.create(title=title, product_class=ic,
+ upc=upc, **kwargs)
+ if price is not None or partner_sku or num_in_stock is not None:
+ if not partner_sku:
+ partner_sku = 'sku_%d_%d' % (item.id, random.randint(0, 10000))
+ if price is None:
+ price = D('10.00')
+
+ partner, __ = Partner._default_manager.get_or_create(name=partner)
+ StockRecord._default_manager.create(product=item, partner=partner,
+ partner_sku=partner_sku,
+ price_excl_tax=price,
+ num_in_stock=num_in_stock)
+ if attributes:
+ for key, value in attributes.items():
+ attr, __ = ProductAttribute.objects.get_or_create(
+ name=key, code=key)
+ ProductAttributeValue.objects.create(
+ product=item, attribute=attr, value=value)
+
+ return item
+
+
+def create_order(number=None, basket=None, user=None, shipping_address=None,
+ shipping_method=None, billing_address=None,
+ total_incl_tax=None, total_excl_tax=None, **kwargs):
+ """
+ Helper method for creating an order for testing
+ """
+ if not basket:
+ basket = Basket.objects.create()
+ basket.add_product(create_product(price=D('10.00')))
+ if not basket.id:
+ basket.save()
+ if shipping_method is None:
+ shipping_method = Free()
+ if total_incl_tax is None or total_excl_tax is None:
+ calc = OrderTotalCalculator()
+ total_incl_tax = calc.order_total_incl_tax(basket, shipping_method)
+ total_excl_tax = calc.order_total_excl_tax(basket, shipping_method)
+ order = OrderCreator().place_order(
+ order_number=number,
+ user=user,
+ basket=basket,
+ shipping_address=shipping_address,
+ shipping_method=shipping_method,
+ billing_address=billing_address,
+ total_incl_tax=total_incl_tax,
+ total_excl_tax=total_excl_tax,
+ **kwargs)
+ basket.set_as_submitted()
+ return order
+
+
+def create_offer(name="Dummy offer", offer_type="Site",
+ max_applications=None, range=None, condition=None,
+ benefit=None):
+ """
+ Helper method for creating an offer
+ """
+ if range is None:
+ range = Range.objects.create(name="All products range",
+ includes_all_products=True)
+ if condition is None:
+ condition = Condition.objects.create(range=range,
+ type=Condition.COUNT,
+ value=1)
+ if benefit is None:
+ benefit = Benefit.objects.create(range=range,
+ type=Benefit.PERCENTAGE,
+ value=20)
+ return ConditionalOffer.objects.create(
+ name=name,
+ offer_type=offer_type,
+ condition=condition,
+ benefit=benefit,
+ max_applications=max_applications)
+
+
+def create_voucher():
+ """
+ Helper method for creating a voucher
+ """
+ voucher = Voucher.objects.create(
+ name="Test voucher",
+ code="test",
+ start_date=datetime.date.today(),
+ end_date=datetime.date.today() + datetime.timedelta(days=12))
+ voucher.offers.add(create_offer())
+ return voucher
@@ -0,0 +1,89 @@
+import httplib
+
+from django.test import TestCase
+from django.test.client import Client
+from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
+from django_webtest import WebTest
+from purl import URL
+
+
+class ClientTestCase(TestCase):
+ """
+ Helper TestCase for using Django's test client. The class provides
+ auto-creation of a user to avoid boilerplate code.
+ """
+ username = 'dummyuser'
+ email = 'dummyuser@example.com'
+ password = 'staffpassword'
+ is_anonymous = False
+ is_staff = False
+ is_superuser = False
+
+ def setUp(self):
+ self.client = Client()
+ if not self.is_anonymous:
+ self.login()
+
+ def login(self):
+ self.user = self.create_user()
+ self.client.login(username=self.username,
+ password=self.password)
+
+ def create_user(self):
+ user = User.objects.create_user(self.username,
+ self.email,
+ self.password)
+ user.is_staff = self.is_staff
+ user.is_superuser = self.is_superuser
+ user.save()
+ return user
+
+ def assertIsRedirect(self, response, expected_url=None):
+ self.assertTrue(response.status_code in (httplib.FOUND,
+ httplib.MOVED_PERMANENTLY))
+ if expected_url:
+ location = URL.from_string(response['Location'])
+ self.assertEqual(expected_url, location.path())
+
+ def assertRedirectUrlName(self, response, name, kwargs=None):
+ self.assertIsRedirect(response)
+ location = response['Location'].replace('http://testserver', '')
+ self.assertEqual(location, reverse(name, kwargs=kwargs))
+
+ def assertIsOk(self, response):
+ self.assertEqual(httplib.OK, response.status_code)
+
+ def assertInContext(self, response, key):
+ self.assertTrue(key in response.context,
+ "Context should contain a variable '%s'" % key)
+
+
+class WebTestCase(WebTest):
+ is_staff = False
+ is_anonymous = True
+ username = 'testuser'
+ email = 'testuser@buymore.com'
+ password = 'somefancypassword'
+
+ def setUp(self):
+ self.user = None
+ if not self.is_anonymous or self.is_staff:
+ self.user = User.objects.create_user(self.username, self.email,
+ self.password)
+ self.user.is_staff = self.is_staff
+ self.user.save()
+
+ def get(self, url, **kwargs):
+ kwargs.setdefault('user', self.user)
+ return self.app.get(url, **kwargs)
+
+ def post(self, url, **kwargs):
+ kwargs.setdefault('user', self.user)
+ return self.app.post(url, **kwargs)
+
+ def assertRedirectsTo(self, response, url_name):
+ self.assertTrue(str(response.status_code).startswith('3'))
+ location = response.headers['Location']
+ redirect_path = location.replace('http://localhost:80', '')
+ self.assertEqual(reverse(url_name), redirect_path)
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+import os
+from setuptools import setup, find_packages
+
+PROJECT_ROOT = os.path.dirname(__file__)
+
+setup(
+ name='django-oscar-testsupport',
+ version='0.1',
+ url='https://github.com/tangentlabs/django-oscar-testsupport',
+ author="David Winterbottom",
+ author_email="david.winterbottom@tangentlabs.co.uk",
+ description="Testing utilities for Oscar",
+ long_description=open(os.path.join(PROJECT_ROOT, 'README.rst')).read(),
+ license=open(os.path.join(PROJECT_ROOT, 'LICENSE')).read(),
+ packages=find_packages(),
+ include_package_data=True,
+ install_requires=[
+ 'django-oscar',
+ 'WebTest>=1.3',
+ 'django-webtest>=1.5',
+ 'purl>=0.4',
+ ])

0 comments on commit d891579

Please sign in to comment.