Creating Tests

Learn how to set up experiments, assign variants, and use common testing patterns with the CADENCE SDK.

Test anatomy

Every CADENCE experiment has:

  • Test name — a unique name (e.g., button-color-test) that you pass to getVariant()
  • Variants — the different versions to test (control + one or more treatments)
  • Traffic allocation — what percentage of users enter the experiment (0–100%)
  • Weights — how traffic is split between variants
  • Visual mutations (optional) — CSS, HTML, or visibility changes configured through the Visual Editor

You create experiments in the CADENCE dashboard, then use the SDK to assign variants and track events in your code.

Two ways to create tests

1. Code Builder (recommended for getting started)

Use the Code Builder in the dashboard to generate test code through a form:

  1. Navigate to your project and click Code Builder
  2. Fill out test name, URL pattern, element selector, and variants
  3. Click Generate Code to get a Cadence.runTest() snippet
  4. Copy the code and add it to your site after Cadence.init()
  5. Click Start Test in the dashboard when ready

The generated code uses Cadence.runTest(), which handles variant assignment, URL matching, and element selection in a single call:

javascript
Cadence.runTest({
  testId: 'pricing-cta-test',
  urlPattern: /\/pricing.*/,
  selector: '#hero-cta',
  variants: [
    {
      name: 'control',
      weight: 0.50,
      apply: function(element) {
        // No changes for control
      }
    },
    {
      name: 'variant-b',
      weight: 0.50,
      apply: function(element) {
        element.textContent = 'Start Free Trial';
        element.style.backgroundColor = '#22c55e';
      }
    }
  ]
});

2. Manual code (more control)

Write test code directly using getVariant() for full control over variant logic.

Getting variants

The core SDK method is getVariant(). It returns the variant name assigned to the current user:

typescript
await cadence.ready()
const variant = cadence.getVariant('button-color-test')
// Returns: 'control' | 'blue-button' | etc.

What happens when you call getVariant()

  1. The SDK looks up the experiment by name
  2. It checks if the user is in the traffic allocation (using deterministic hashing)
  3. If yes, it assigns a variant based on weights
  4. It automatically tracks an exposure event
  5. If the variant has visual mutations, it applies them to the DOM
  6. It returns the variant name as a string

Deterministic assignment

Variant assignment is based on a hash of userId + experimentId. The same user always gets the same variant for the same experiment — no database lookups, no randomness. This works offline and is consistent across page loads.

If the experiment doesn't exist

If you call getVariant() with an experiment name that doesn't exist (or hasn't started), it returns 'control'. This means your code always has a safe default.

Common test patterns

Button colors

typescript
const variant = cadence.getVariant('cta-button-color')

switch (variant) {
  case 'green':
    return <button className="bg-green-600 text-white">Sign Up</button>
  case 'orange':
    return <button className="bg-orange-500 text-white">Sign Up</button>
  default: // control
    return <button className="bg-blue-600 text-white">Sign Up</button>
}

Copy variations

typescript
const variant = cadence.getVariant('hero-headline')

const headlines = {
  control: 'Start your free trial',
  urgency: 'Start your free trial — limited time',
  social: 'Join 10,000+ teams who test with CADENCE',
}

return <h1>{headlines[variant] || headlines.control}</h1>

Pricing tests

typescript
const variant = cadence.getVariant('pricing-test')

const prices = {
  control: { monthly: 29, annual: 290 },
  higher: { monthly: 39, annual: 390 },
  anchored: { monthly: 49, annual: 290 },  // Higher monthly, same annual
}

const pricing = prices[variant] || prices.control

Be careful with pricing tests

Make sure your payment system uses the server-side price, not the client-side displayed price. A user could inspect the page and see the variant price. Always validate on the server.

Multi-variant tests (A/B/C/D)

CADENCE supports more than two variants. In the dashboard, add as many variants as needed and set their traffic weights:

typescript
const variant = cadence.getVariant('onboarding-flow')

// Could be: 'control', 'short-form', 'video-intro', 'interactive-tour'
switch (variant) {
  case 'short-form':
    return <ShortOnboarding />
  case 'video-intro':
    return <VideoOnboarding />
  case 'interactive-tour':
    return <InteractiveOnboarding />
  default:
    return <StandardOnboarding />
}

Tracking events

Automatic exposure tracking

getVariant() automatically tracks an exposure event. You don't need to call anything extra. This tells CADENCE which users saw which variant.

Conversion tracking

Track goal completions with trackConversion():

typescript
cadence.trackConversion('signup')
cadence.trackConversion('purchase', { value: 49.99, plan: 'pro' })

For a deep dive into event types, batching, and best practices, see Event Tracking.

Feature flags

CADENCE also supports feature flags for gradual rollouts. For details on isFeatureEnabled() and getFeatureValue(), see Feature Flags.

Naming conventions

Use kebab-case for experiment names and event names:

| Good | Bad | |------|-----| | hero-cta-color | heroCTAColor | | signup-flow-v2 | Signup Flow V2 | | purchase-complete | purchase_complete |

Be descriptive. Your future self (and your team) will thank you when reading results.

Best practices

One change at a time

Each test should isolate a single variable. If you change the button color AND the headline at the same time, you won't know which change drove the result.

  1. Calculate sample size first. Use a sample size calculator to determine how many users you need per variant. Running a test with too few users gives unreliable results.

  2. Run tests for at least 1–2 full business cycles. User behavior varies by day of week. Running Monday through Sunday captures the full pattern.

  3. Don't peek at results. Checking results repeatedly and stopping when you see significance leads to false positives (p-hacking). Set a duration and stick to it.

  4. Use consistent user IDs. Pass your application's user ID to the SDK so the same user always sees the same variant across sessions and devices.

Don't change tests mid-flight

Changing variant weights, adding variants, or modifying traffic allocation while a test is running invalidates the results. If you need changes, stop the test, make changes, and start a new one.

Cleanup

When your component unmounts, call destroy() to flush remaining events:

typescript
useEffect(() => {
  // ... setup
  return () => {
    cadence.destroy()
  }
}, [])

Next steps