5

This is Django 1.6.8, Python 2.7, and the mock library.

I have a view that calls a remote service using suds for sales tax information (this is a simplified version):

def sales_tax(self, bundle_slug):
    bundle = get_object_or_404(Bundle, slug=bundle_slug,
                                active=True)

    cloud = TaxCloud(TAXCLOUD_API_ID, TAXCLOUD_API_KEY)
    origin = Address('origin address')
    destination = Address('customer address')
    cart_item = CartItem(bundle.sku, TAXCLOUD_TIC_ONLINE_GAMES,
                        bundle.price, 1)

    try:
        rate_info = cloud.get_rate(origin, destination,
                                    [cart_item],
                                    str(customer.id))

        sales_tax = Decimal(rate_info['sales_tax'])

        response = {'salesTax': locale.currency(sales_tax),
                    'total':  locale.currency(bundle.price + sales_tax)}
    except TaxCloudException as tce:
        response = {
            'error': str(tce)
        }

Here's relevant code from the TaxCloud class:

from suds.client import Client
from suds import WebFault

class TaxCloud(object):

    def __init__(self, api_login_id, api_key):
        self.api_login_id = api_login_id
        self.api_key = api_key
        self.soap_url = 'https://api.taxcloud.net/1.0/TaxCloud.asmx'
        self.wsdl_url = 'https://api.taxcloud.net/1.0/?wsdl' 

        self.client = Client(url=self.wsdl_url, location=self.soap_url, faults=False)

    def get_rate(self, billing_address, origin_address, raw_cart_items, customer_id):
        address = self.convert_to_address(billing_address)
        origin = self.convert_to_address(origin_address)
        cart_items = self.convert_to_cart_list(raw_cart_items)

        response = self.client.service.Lookup(self.api_login_id,
                                              self.api_key, customer_id,
                                              self.cart_id(customer_id), cart_items,
                                              address, origin, True, None)

        if( response[1].ResponseType == 'Error' ):
            raise TaxCloudException(response[1].Messages[0][0].Message)

        return {
            'sales_tax': str(response[1].CartItemsResponse[0][0].TaxAmount),
            'cart_id': response[1].CartID
        }

In my test for the view, I don't want to call the remote service. Using this example of a mocked client, I built out a dumb mock class (my ClientMock matches exactly the example in that answer):

class TaxCloudServiceClientMock(ClientMock):
    """
    Mock object that implements remote side services.
    """

    def Lookup(cls, api_id, api, customer_id, cart_id, cart_items,
               address, origin, flag, setting):
        """
        Stub for remote service.
        """
        return """(200, (LookupRsp){
               ResponseType = "OK"
               Messages = ""
               CartID = "82cabf35faf66d8b197c7040a9f7382b3f61573fc043d73717"
               CartItemsResponse =
                  (ArrayOfCartItemResponse){
                     CartItemResponse[] =
                        (CartItemResponse){
                           CartItemIndex = 1
                           TaxAmount = 0.10875
                        },
                  }
                })"""

In my test, I'm trying to @patch the Client used in the TaxCloud class:

@patch('sales.TaxCloud.Client', new=TaxCloudServiceClientMock)
def test_can_retrieve_sales_tax(self):
    from django.core.urlresolvers import reverse

    tax_url = reverse('sales_tax', kwargs={'bundle_slug': self.bundle.slug})
    self.client.login(username=self.user.username, password='testpassword')

    response = self.client.get(tax_url, {'address1': '1234 Blah St',
                                        'city': 'Some City',
                                        'state': 'OH',
                                        'zipcode': '12345',
                                        'country': 'US'},
        HTTP_X_REQUESTED_WITH='XMLHttpRequest')

    self.assertEqual(response.status_code, 200)

The remote call is still being made, however. Based on the "Where to mock" documentation, I'm correctly targeting sales.TaxCloud.Client instead of suds.client.Client.

What could be causing the patch to be ignored/bypassed?

Community
  • 1
  • 1
Melissa Avery-Weir
  • 1,357
  • 2
  • 16
  • 47
  • 1
    I'm not a Django expert, but my guess is that `TaxCloud` object is not build at request time but a django service init time. You should try to `patch` `sales.TaxCloud.Client.service.Lookup` instead and use `return_value` instead of your own new mock. – Michele d'Amico May 29 '15 at 14:05
  • @melissa did you find a solution for this? – cph2117 Apr 27 '17 at 23:58
  • @cph2117 Unfortunately not. We've been able to mock other service calls just fine using the method I describe above, but not this. – Melissa Avery-Weir May 07 '17 at 19:08

0 Answers0