【问题标题】:Mock Stripe Methods in Python for testingPython中用于测试的模拟条纹方法
【发布时间】:2015-09-25 21:19:40
【问题描述】:

所以我试图模拟方法中的所有stripe web hooks,以便我可以为它编写Unit test。我正在使用mock library 来模拟条纹方法。这是我试图模拟的方法:

class AddCardView(APIView):
"""
* Add card for the customer
"""

permission_classes = (
    CustomerPermission,
)

def post(self, request, format=None):
    name = request.DATA.get('name', None)
    cvc = request.DATA.get('cvc', None)
    number = request.DATA.get('number', None)
    expiry = request.DATA.get('expiry', None)

    expiry_month, expiry_year = expiry.split("/")

    customer_obj = request.user.contact.business.customer

    customer = stripe.Customer.retrieve(customer_obj.stripe_id)

    try:
        card = customer.sources.create(
            source={
                "object": "card",
                "number": number,
                "exp_month": expiry_month,
                "exp_year": expiry_year,
                "cvc": cvc,
                "name": name
            }
        )
        # making it the default card
        customer.default_source = card.id
        customer.save()
    except CardError as ce:
        logger.error("Got CardError for customer_id={0}, CardError={1}".format(customer_obj.pk, ce.json_body))
        return Response({"success": False, "error": "Failed to add card"})
    else:
        customer_obj.card_last_4 = card.get('last4')
        customer_obj.card_kind = card.get('type', '')
        customer_obj.card_fingerprint = card.get('fingerprint')
        customer_obj.save()

    return Response({"success": True})

这是unit testing的方法:

@mock.patch('stripe.Customer.retrieve')
@mock.patch('stripe.Customer.create')
def test_add_card(self,create_mock,retrieve_mock):
    response = {
        'default_card': None,
        'cards': {
            "count": 0,
            "data": []
        }
    }

    # save_mock.return_value = response
    create_mock.return_value = response
    retrieve_mock.return_value = response

    self.api_client.client.login(username = self.username, password = self.password)
    res = self.api_client.post('/biz/api/auth/card/add')

    print res

现在stripe.Customer.retrieve 正在被正确地模拟。但我无法模拟customer.sources.create。我真的被困在这上面了。

【问题讨论】:

    标签: unit-testing django-testing python-mock


    【解决方案1】:

    这是正确的做法:

    @mock.patch('stripe.Customer.retrieve')
    def test_add_card_failure(self, retrieve_mock):
        data = {
            'name': "shubham",
            'cvc': 123,
            'number': "4242424242424242",
            'expiry': "12/23",
        }
        e = CardError("Card Error", "", "")
        retrieve_mock.return_value.sources.create.return_value = e
    
        self.api_client.client.login(username=self.username, password=self.password)
    
        res = self.api_client.post('/biz/api/auth/card/add', data=data)
    
        self.assertEqual(self.deserialize(res)['success'], False)
    

    【讨论】:

    • 谢谢舒巴姆。你能举例说明模拟是如何工作的吗?
    【解决方案2】:

    即使给出的答案是正确的,也有一种使用vcrpy 的更舒适的解决方案。这就是在给定记录尚不存在时创建一个cassette(记录)。当它这样做时,模拟是透明地完成的,并且记录将被重放。美丽的。

    拥有一个普通的金字塔应用程序,使用 py.test,我的测试现在看起来像这样:

    import vcr 
    # here we have some FactoryBoy fixtures   
    from tests.fixtures import PaymentServiceProviderFactory, SSOUserFactory
    
    def test_post_transaction(sqla_session, test_app):
        # first we need a PSP and a User existent in the DB
        psp = PaymentServiceProviderFactory()  # type: PaymentServiceProvider
        user = SSOUserFactory()
        sqla_session.add(psp, user)
        sqla_session.flush()
    
        with vcr.use_cassette('tests/casettes/tests.checkout.services.transaction_test.test_post_transaction.yaml'):
            # with that PSP we create a new PSPTransaction ...
            res = test_app.post(url='/psps/%s/transaction' % psp.id,
                                params={
                                    'token': '4711',
                                    'amount': '12.44',
                                    'currency': 'EUR',
                                })
            assert 201 == res.status_code
            assert 'id' in res.json_body
    

    【讨论】:

    • 我只能推荐人VCRpy。这是一个超级棒的库,它帮助我们大大减少了测试时间,最重要的是它很容易集成到我们的 Django 测试中
    • 我在 Ruby 中广泛使用了 VCR,但不知道 Python 中有一个端口。谢谢你,@pansen!
    • 我无法想象为什么有人会长期推荐 VCR。这是一种懒惰的方法,而且你会在维护/重新生成磁带时遇到更多的问题(更不用说在版本控制中的响应中存储所有额外内容),而不是仅仅以正确的方式进行操作并使用类似 httmock 或等效的东西正确地外部化依赖关系。
    【解决方案3】:

    IMO,以下方法比其他答案更好

    import unittest
    import stripe
    import json
    from unittest.mock import patch
    from stripe.http_client import RequestsClient # to mock the request session
    
    stripe.api_key = "foo"
    
    stripe.default_http_client = RequestsClient() # assigning the default HTTP client
    
    null = None
    false = False
    true = True
    charge_resp = {
        "id": "ch_1FgmT3DotIke6IEFVkwh2N6Y",
        "object": "charge",
        "amount": 1000,
        "amount_captured": 1000,
        "amount_refunded": 0,
        "billing_details": {
            "address": {
                "city": "Los Angeles",
                "country": "USA",
            },
            "email": null,
            "name": "Jerin",
            "phone": null
        },
        "captured": true,
    }
    
    
    def get_customer_city_from_charge(stripe_charge_id):
        # this is our function and we are writing unit-test for this function
        charge_response = stripe.Charge.retrieve("foo-bar")
        return charge_response.billing_details.address.city
    
    
    class TestStringMethods(unittest.TestCase):
    
        @patch("stripe.default_http_client._session")
        def test_get_customer_city_from_charge(self, mock_session):
            mock_response = mock_session.request.return_value
            mock_response.content.decode.return_value = json.dumps(charge_resp)
            mock_response.status_code = 200
    
            city_name = get_customer_city_from_charge("some_id")
            self.assertEqual(city_name, "Los Angeles")
    
    
    if __name__ == '__main__':
        unittest.main()

    这种方法的优点

    1. 可以生成对应的类对象(这里charge_response变量是Charge--(source code)的类型)
    2. 您可以在响应上使用 点 (.) 运算符(就像我们可以使用 real stripe SDK 一样)
    3. 点运算符支持深层属性

    【讨论】:

    • 这个解决方案对我有用,谢谢!,现在我在嘲笑错误响应时遇到了麻烦,但它是最好的响应。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-20
    • 2018-07-12
    • 1970-01-01
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多