---
title: "Testing"
canonical: "https://aifitnessapi.com/test"
type: "cluster-index"
pages: "14"
last_reviewed: "2026-07-27"
publisher: "AIFitnessAPI"
---

# Testing

> 14 pages. Each entry below shows the question the page owns, followed by its answer capsule.

## How to Test a HealthKit Integration

- Question: how to test healthkit integration
- HTML: https://aifitnessapi.com/test/healthkit-integration
- Markdown: https://aifitnessapi.com/test/healthkit-integration.md
- Last reviewed: 2026-07-27

The assertion worth writing is about your arithmetic, not Apple's framework: given a fixture set of overlapping iPhone and Watch samples, your daily total must not be their sum. Apple ships no HealthKit test double: no fake store, no test mode, no way to seed ordinary samples into the Simulator. So define a narrow protocol seam yourself and keep the deduplication, timezone and rollup logic behind it as pure functions that need no store at all. Keep XCUITest for two or three end-to-end paths, and test the empty result as a first-class path rather than an error, because HealthKit hides read authorization and a denied read is indistinguishable from no data.

## Test Data for Health Connect: Fakes, the Toolbox, and the Generators You Write

- Question: health connect test data
- HTML: https://aifitnessapi.com/test/health-connect-test-data
- Markdown: https://aifitnessapi.com/test/health-connect-test-data.md
- Last reviewed: 2026-07-27

Insert records into FakeHealthConnectClient, read them back with a page size of 2, and assert every record arrives exactly once — pagination, change tokens, permission checks and thrown exceptions are what the library genuinely proves. The constraint is that androidx.health.connect:connect-testing is still 1.0.0-alpha03, released April 9 2025, ships no fake-data generation API, and stubs aggregation rather than computing it, so a daily-total assertion made through the fake only re-reads the number you handed it. Move the arithmetic into your own pure function and test that against records instead. Use the Toolbox by hand on a device for exploration, never in CI, and write your own generators for the ugly multi-source fixtures.

## Mock Wearable Data That Is Ugly Enough to Find Bugs

- Question: mock wearable data for testing
- HTML: https://aifitnessapi.com/test/mock-wearable-data
- Markdown: https://aifitnessapi.com/test/mock-wearable-data.md
- Last reviewed: 2026-07-27

Your fixtures have to be ugly enough that idempotence, coverage and preserved absence can actually fail — clean synthetic data cannot fail any of them, which is why a green suite coexists with a Saturday that reads double. The constraint is that no platform will build those fixtures for you. Google documents that the Health Connect testing library ships no fake-data generation API and stubs aggregation rather than computing it, and Apple ships no HealthKit test double at all, so the generator is yours. Write a seeded generator that emits overlaps, clock skew, retro-edits, gaps, DST days and denied reads, and take the magnitudes from your own production data rather than from anyone's published ranges.

## Testing Background Sync: Three Tests, and Only One Is Real

- Question: test healthkit background delivery
- HTML: https://aifitnessapi.com/test/background-sync
- Markdown: https://aifitnessapi.com/test/background-sync.md
- Last reviewed: 2026-07-27

The assertion worth writing is that your wake handler never advances its cursor on a failed read and produces the same result when the same wake arrives twice, expressed as a total function over samples, an error and a stored cursor. Apple documents that background server queries are not supported on the Simulator, so no hosted CI run can prove the delivery itself ever happens. Test that handler exhaustively and alert on silence, rather than writing an integration test that pretends CI woke your app up.

## Testing an OAuth Integration: The Token Lifecycle, Not the Login Screen

- Question: how to test oauth integration third party api
- HTML: https://aifitnessapi.com/test/oauth-flows
- Markdown: https://aifitnessapi.com/test/oauth-flows.md
- Last reviewed: 2026-07-27

Write four assertions against recorded fixtures. A 401 triggers exactly one refresh and one retry; a rotated refresh token is persisted before anything else runs; an invalid_grant flips the connection to revoked rather than retrying forever; a user-initiated disconnect leaves no usable token behind. RFC 7009 shapes all of it: a revocation endpoint returns 200 even for a token that was never valid, so assert consequences — including the one nobody writes, which is that a day with no samples because the grant was dead is not a day with zero steps. Drive the suite from fixtures rather than pointing CI at a live provider account you will eventually get locked out of.

## Provider Sandboxes: Build the Fake, Keep One Real Account

- Question: fitbit api sandbox
- HTML: https://aifitnessapi.com/test/provider-sandboxes
- Markdown: https://aifitnessapi.com/test/provider-sandboxes.md
- Last reviewed: 2026-07-27

Write your assertions against a local fake you control, and keep exactly one real staging account per provider for a contract test a human runs on purpose. As of 2026-07-30 we could confirm a first-party test tool only for Android Health Connect, and the absence of a hosted sandbox only for Apple HealthKit. For the seven cloud providers we could not reach the documentation to check either way, so verify those against the provider's own current docs. Point continuous integration at the fake, never at a live provider, because a real account's data is a real person's body and it retro-edits itself underneath your assertions.

## Testing Webhooks Locally: Replay Signed Payloads, Not Just the Handshake

- Question: test strava webhook locally
- HTML: https://aifitnessapi.com/test/webhooks-locally
- Markdown: https://aifitnessapi.com/test/webhooks-locally.md
- Last reviewed: 2026-07-27

Write a test that POSTs the same signed delivery twice and asserts the user's day is unchanged and exactly one fetch job was enqueued. The constraint that shapes it is that most fitness webhooks are thin change pointers rather than data, so your handler's only real job is enqueueing work, and a duplicate processed twice silently doubles a step count instead of raising anything. Assert on the effect, not the status code: a correct handler returns 200 to a duplicate by design, so a test that checks for 200 twice cannot fail.

## Testing 429 Rate-Limit and Outage Handling in a Health Backfill

- Question: test api rate limit handling 429
- HTML: https://aifitnessapi.com/test/rate-limits-and-outages
- Markdown: https://aifitnessapi.com/test/rate-limits-and-outages.md
- Last reviewed: 2026-07-27

Point the fault injection at a historical backfill rather than at your retry helper, and assert observable outcomes: no date window silently skipped, a resumed job that re-requests zero completed windows, a bounded retry budget, and recovery traffic that is spread rather than synchronised. RFC 6585 section 4 defines 429 and makes Retry-After a MAY, so a named no-header case with your own backoff is mandatory, not a nice-to-have. Provider quotas are provider-specific and frequently unpublished, so design the job to degrade instead of tuning it to a figure. Test the slow response too, because it ties up workers silently while an outage at least fails loudly.

## Testing Camera Features When You Have No Camera

- Question: test camera app ios simulator
- HTML: https://aifitnessapi.com/test/camera-features-without-a-device
- Markdown: https://aifitnessapi.com/test/camera-features-without-a-device.md
- Last reviewed: 2026-07-27

Write the assertion against a recorded video fixture rather than against a camera: push a known workout clip through the shipping pipeline and assert the rep count. Apple's AVCam documentation states that Simulator has no access to device cameras, and the Android emulator offers only a host webcam or an imported PNG or JPEG still, so a file is the only frame source both platforms can drive in CI. That forces one decision on day one, namely that every frame enters through an injectable frame source, because retrofitting the seam means re-deriving timestamps, orientation, backpressure and end-of-stream, which is a rewrite and not a refactor. On iOS, run Vision requests over the file with VNVideoProcessor instead of trying to give the Simulator a camera.

## Testing Pose Estimation Accuracy with a Regression Corpus

- Question: how to test pose estimation accuracy
- HTML: https://aifitnessapi.com/test/pose-detection-accuracy
- Markdown: https://aifitnessapi.com/test/pose-detection-accuracy.md
- Last reviewed: 2026-07-27

Write a test that replays a fixed set of labelled clips through an explicitly pinned model revision and fails when any keypoint your product actually reads drifts outside a per-keypoint tolerance. The constraint is that no published benchmark figure tells you anything about your camera, your exercises or your users, so every threshold has to be derived from footage you labelled yourself and from the displacement at which your own rep verdict flips. Score per keypoint rather than as one aggregate, because a mean over nineteen joints hides the ankle regression that breaks your squat counter. And label occlusion as a state rather than scoring it as a miss, or the suite will punish the model for correctly admitting it cannot see a hidden joint.

## How to Test a Rep Counting Algorithm

- Question: test rep counting algorithm
- HTML: https://aifitnessapi.com/test/rep-counting
- Markdown: https://aifitnessapi.com/test/rep-counting.md
- Last reviewed: 2026-07-27

Score a rep counter against a labelled corpus as a classifier, but do not gate on aggregate precision and recall — gate on per-clip baseline movement, because two clips can break in opposite directions while the aggregate sits perfectly still. Comparing final counts per clip is weaker still: a miss and a double-count cancel and the suite passes on a counter that is wrong twice. Miscounts are uniquely damaging because the user was counting along in their own head and knows you are wrong.

## CI for an App That Needs a Real Device

- Question: ci for app that needs real device
- HTML: https://aifitnessapi.com/test/device-lab-and-ci
- Markdown: https://aifitnessapi.com/test/device-lab-and-ci.md
- Last reviewed: 2026-07-27

The assertion that justifies a device lab is a thermal soak: run one reference clip through the live capture pipeline eight times back to back on a physical phone. Fail the build if the count or the p95 frame latency drifted from pass one — no container can produce that number, and on iOS no simulator can produce a camera frame at all. Keep hosted CI for everything deterministic given bytes, and pick the device matrix by criteria rather than by model name — oldest supported chipset, weakest accelerator, one per camera-stack generation, one with a thermal ceiling. Run a small matrix on every build rather than a large one once a month.

## Testing Offline Sync and Conflict Resolution

- Question: test offline sync conflict mobile app
- HTML: https://aifitnessapi.com/test/offline-sync
- Markdown: https://aifitnessapi.com/test/offline-sync.md
- Last reviewed: 2026-07-27

Two sync engines in one test process, a fake server that holds no merge logic, an injected clock per client, and a seeded list of operations — that harness is the test. It is the only cheap way to reproduce what actually costs data: a watch and a phone both logging sets in a basement with no signal, flushing hours apart. Assert conservation on everything a person deliberately typed, and keep device-sourced samples in a separate suite, because there the assertion is the opposite one.

## Testing That a User's Health Data Is Actually Deleted

- Question: how to verify user data deleted
- HTML: https://aifitnessapi.com/test/data-deletion
- Markdown: https://aifitnessapi.com/test/data-deletion.md
- Last reviewed: 2026-07-27

Write one assertion per store, with the store list enumerated from a registry checked into the repo, so the suite fails the day someone adds a table and forgets to purge it. The constraint that shapes every assertion here is RFC 7009: a token revocation endpoint returns HTTP 200 even for an invalid token, so a test that asserts revocation returned 200 cannot fail and proves nothing. Assert on the observable consequence instead — the next provider call fails, no new samples arrive after the tombstone, the connection row is gone. Never assert on the purge job's return value; assert on the stores it was supposed to empty.
