Skip to main content

How to Test Tax, VAT, and Country-Specific Pricing

Short answer

Tax errors create compliance exposure and cart abandonment. E2E must probe order line items and tax amounts in minor units for seeded billing countries and VAT IDs—not translated "Tax" labels. Combine geo fixtures, Stripe Tax test mode where used, and TrueCoverage on country × tax_jurisdiction × currency slices prod actually checks out with.

Part of Testing Guides by business flow.

Who this is for

Global ecommerce and SaaS with localized prices, VAT/GST, US sales tax, or B2B reverse-charge who bill through Stripe Tax, Avalara, or custom rate tables.

Why testing tax matters

  • Compliance — wrong VAT on B2B exempt accounts; missing OSS reporting data.
  • Revenue — under-tax erodes margin; over-tax abandons carts.
  • Support — "Price changed at checkout" from geo IP vs billing address mismatch.

Complexity map

ScenarioEdge caseWhy tests breakApproach
EU B2C VATDE billing addressRate wrongSeed DE postal; probe tax rate
B2B reverse chargeValid VAT IDTax still appliedSeed exempt org + VIES-valid ID
US nexusState + zipSurprise taxSeed CA zip; probe state tax line
Geo IP vs billingVPN countryWrong price bookFix billing country in Arrange
Currency JPYNo decimalsFloat driftProbe integer minor units
RoundingLine vs order total1¢ offProbe sum(lines) === total
Inclusive vs exclusiveDisplay vs chargeCustomer disputeProbe tax_behavior on lines
Digital services MOSSCross-border EUWrong jurisdictionSeed EU customer countries
Price bookCountry-specific SKUDefault USD shownSeed geo; probe price_id
Tax-exempt certExpired certTax removed incorrectlySeed cert expiry date
Subscription taxRecurring invoicesFirst invoice only testedProbe renewal invoice tax
Refund taxPartial refundTax not reversedSee returns guide

Arrange: geo and tax fixtures

// POST /api/test/seed-customer-tax-profile
// { runId, country: 'DE', postalCode: '10115', vatId: 'DE123456789', taxExempt: false }

await request.post('/api/test/seed-customer-tax-profile', {
data: { runId, country: 'DE', postalCode: '10115' },
});

Override geo IP in test env (X-Test-Country: DE) so tests do not depend on CI runner location.

EU VAT checkout

await request.post('/api/test/seed-customer-tax-profile', {
data: { runId, country: 'DE', postalCode: '10115' },
});
await page.goto(`/checkout?runId=${runId}`);
// Complete address + payment — card path from stripe guide
await completeStripeCheckout(page, runId);

const order = await request.get(`/api/test/probes/orders/${runId}`).then(r => r.json());
expect(order.taxLines[0].jurisdiction).toMatch(/DE/i);
expect(order.taxLines[0].rate).toBeCloseTo(0.19, 2);
expect(order.totalTax).toBe(Math.round(order.subtotal * 0.19));

B2B exempt

await request.post('/api/test/seed-customer-tax-profile', {
data: { runId, country: 'DE', vatId: 'DE811128135', taxExempt: true },
});
// ... checkout ...
const order = await request.get(`/api/test/probes/orders/${runId}`).then(r => r.json());
expect(order.totalTax).toBe(0);
expect(order.reverseCharge).toBe(true);

Currency rounding

CurrencyAssert
USDCents integer
JPYZero decimal places
BHD3 decimal minor units

Probe amount_minor fields—never toBeCloseTo on floats without minor-unit conversion.

Stripe Tax

If using Stripe Tax, enable test mode tax registrations in dashboard; probe Checkout Session total_details.amount_tax via your backend mirror.

Anti-patterns

Anti-patternWhy it failsBetter approach
Assert UI "VAT 19%"i18n/copy changesProbe tax line rate
Single US address onlyEU prod majoritySeed country matrix
Hard-code ratesLaw changesProbe against config version id
Skip exempt pathB2B invoices wrongDedicated exempt scenario

Example scenario

Situation: German B2B customer with valid VAT ID checks out.

Expected outcome: Reverse charge applied; invoice shows zero VAT and valid VAT IDs.

Why UI-only automation breaks: Checkout shows €0 tax but invoice PDF still includes VAT line.

  1. Arrange: Seed org with DE VAT ID and taxExempt verified.
  2. Act: Complete checkout with billing address in DE.
  3. Assert: Probe totalTax=0, reverseCharge=true, customer and seller VAT on invoice metadata.

TestChimp workflow: Compare country × tax_jurisdiction × currency in TrueCoverage—expand when DE B2B share grows.

Same Arrange/Act/Assert pattern as expired-coupon checkout.

Connect scenarios to your QA workflow

Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).

External references

Frequently asked questions

How do I test VAT for EU countries in E2E?

Seed customer with EU billing country and VAT ID, complete checkout, probe order tax lines match expected rate for that jurisdiction—do not hard-code UI labels only.

How do I test B2B reverse charge?

Seed validated VAT ID with taxExempt flag, checkout, probe totalTax zero and reverseCharge or equivalent metadata on order and invoice.

Geo IP shows wrong country in CI—what do I do?

Set test header or seed billing address in Arrange; never rely on CI runner IP for tax assertions.

Should tax tests use Stripe Tax or mock rates?

Integration E2E should use same engine as prod—Stripe Tax test registrations or your Avalara sandbox—not hard-coded mocks in E2E.

How do I test JPY rounding?

Seed JPY price book, checkout, probe all amounts integer in minor units with zero decimal currency exponent.

Do subscriptions need separate tax scenarios?

Yes—first invoice and renewal can differ when address changes; probe invoice.tax on renewal after test clock advance.

We sell in 12 countries—how do we prioritize test coverage?

Use TrueCoverage on country × tax_jurisdiction × currency from prod checkout metadata. Run /testchimp evolve for missing top slices; link // @Scenario: per country.

Apply these patterns in your repo

Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.

Start free on TestChimp · Book a demo