Mocking Stripe signature checks in a pytest fixture
I’m writing some code that accepts webhooks from Stripe. I wanted to simulate hits to this endpoint in my Django tests. Stripe uses a Stripe-Signature header and I wanted a way to mock my code so that I didn’t need to calculate the correct signature.
Here’s the pattern I used:
import pytestfrom unittest.mock import patch
@pytest.fixturedef mock_stripe_verify_header(): with patch("stripe.WebhookSignature.verify_header") as mock_verify: mock_verify.return_value = None yield mock_verifyThis gives me a mock_stripe_verify_header fixture which I can pass to a test function in order to cause the verify_header() method in the Stripe library to always pass - as opposed to raising exceptions, which it does when the signature check fails. The corresponding Django view code that uses this API does so via stripe.Webhook.construct_event() and looks like this:
@csrf_exempt@require_POSTdef stripe_webhook(request): payload = request.body sig_header = request.META["HTTP_STRIPE_SIGNATURE"] try: event = stripe.Webhook.construct_event( payload, sig_header, settings.STRIPE_WEBHOOK_SECRET ) except (ValueError, stripe.error.SignatureVerificationError) as ex: # Invalid payload or signature return HttpResponse(status=400, content=json.dumps({"error": str(ex)}))Now I can write tests like this one:
STRIPE_PAYLOAD = { "id": "evt_test123", "object": "event", "type": "payment_intent.succeeded", "created": 1630000000, "data": { "object": { "id": "pi_test123", "object": "payment_intent", "amount": 1000, "currency": "usd", } }, "livemode": False, "api_version": "2020-08-27",}
@pytest.mark.django_dbdef test_valid_webhook(client, mock_stripe_verify_header): response = client.post( WEBHOOK_URL, data=json.dumps(STRIPE_PAYLOAD), content_type="application/json", HTTP_STRIPE_SIGNATURE="ignored", ) assert response.status_code == 200Or if I want to test what happens when the signature check fails, I can use mock_stripe_verify_header.side_effect to cause it to trigger the expected exception:
def test_invalid_webhook(client, mock_stripe_verify_header): mock_stripe_verify_header.side_effect = SignatureVerificationError( "Invalid signature", sig_header="ignored" ) response = client.post( WEBHOOK_URL, data=json.dumps(STRIPE_PAYLOAD), content_type="application/json", HTTP_STRIPE_SIGNATURE="ignored", ) assert response.status_code == 400 assert response.content == b'{"error": "Invalid signature"}'