Skip to main content

How to Test Real Estate Search and Listing Flows

Short answer

Property portals fail when map pins and list results desync, stale listings remain searchable after sold, saved searches leak across users, and geo filters return wrong bounds—not when a search box accepts text. Seed listings with known coordinates, assert listing id sets match between map and list probes, and prioritize high-traffic filter combos via TrueCoverage search_filters dimension.

Part of Testing Guides by industry.

Who this is for

Teams shipping MLS-powered portals, rental marketplaces, commercial property search, or map-first listing UIs with price/beds/baths filters, draw-on-map search, saved searches, and lead forms.

Why testing real estate listings matters

Search bugs directly affect broker revenue and consumer trust:

  • Map/list desync — pin visible, listing missing from list; users miss properties or click ghosts.
  • Stale sold listings — status sold but still in search index; wasted tours and MLS compliance issues.
  • Wrong geo filter — draw polygon excludes interior pins; fair housing scrutiny if certain neighborhoods systematically dropped.
  • Saved search alerts — cross-user leak of saved criteria; privacy incident.
  • Lead routing — inquiry attached to wrong agent or listing id; commission disputes.

Probe listing id sets and listing_status—map marker count alone is unreliable with clustering.

Complexity map

ScenarioEdge caseWhy tests breakApproach
Map/list syncCluster hides countMismatchProbe same id set
Sold staleIndex lagGhost listingProbe status + search
Price+beds comboAND logic wrongFalse emptySeed tagged listings
Draw polygonEdge pinsBoundary bugProbe geo contains
Saved searchUser A sees BACLProbe owner user_id
Pagination map moveNew boundsDuplicate fetchAssert unique ids
Sort price descWrong orderBad comparablesProbe sorted ids
New listing alertEmail/in-appMissing notifyInject listing probe
Photo carouselCDN 404Broken detailHEAD probe optional
MLS sync delayPending statusPremature publicProbe visibility flag
School district filterBoundary dataWrong inclusionFixture district ids
Rental vs saleWrong modeMixed resultsProbe listing_type

Geo listing seed

await request.post('/api/test/seed-listings', {
data: {
runId,
listings: [
{ id: 'L1', lat: 37.77, lng: -122.42, price: 500000, beds: 2, status: 'active' },
{ id: 'L2', lat: 37.78, lng: -122.43, price: 800000, beds: 3, status: 'active' },
{ id: 'L3', lat: 37.77, lng: -122.42, price: 600000, beds: 2, status: 'sold' },
],
},
});

Map/list sync Assert

await page.goto('/search/san-francisco');
await page.getByLabel('Min beds').fill('2');
await page.getByRole('button', { name: 'Search' }).click();

const probe = await request.get(`/api/test/search?runId=${runId}&beds=2&status=active`).then(r => r.json());
expect(probe.ids).toEqual(expect.arrayContaining(['L1', 'L2']));
expect(probe.ids).not.toContain('L3');

const listIds = await page.getByTestId('listing-card').evaluateAll(nodes =>
nodes.map(n => n.getAttribute('data-listing-id'))
);
expect(new Set(listIds)).toEqual(new Set(probe.ids));

// Map: click pin for L1
await page.getByTestId('map-pin-L1').click();
await expect(page.getByTestId('listing-card').filter({ has: page.locator('[data-listing-id="L1"]') })).toBeVisible();

For Google Maps embeds see Google Maps integration—may require probe-first strategy when pins are canvas-rendered.

await page.getByRole('button', { name: 'Save search' }).click();
const saved = await request.get(`/api/test/saved-searches?runId=${runId}`).then(r => r.json());
expect(saved).toHaveLength(1);
expect(saved[0].criteria.beds).toBe(2);
// Negative: other user
const other = await request.get(`/api/test/saved-searches?runId=${runId}-other`);
expect((await other.json())).toHaveLength(0);

Requirement slices to cover

  • search_filters — normalized combo e.g. beds:2|price:500k-800k|type:sale

Pair with search filters pattern for facet explosion strategy.

CI checklist

  1. Listings with lat/lng + status in seed
  2. Map/list id set equality via probe
  3. Sold listing excluded from active search probe
  4. Saved search ACL negative
  5. Avoid real MLS keys in CI—fixture index only
  6. ExploreChimp mobile map/list split view

Anti-patterns

Anti-patternWhy it failsBetter approach
Count map pins visuallyClusteringProbe ids
Prod MLS in CIRate/legalSeed API
List text onlyMap desyncId set match
Skip sold staleGhost toursstatus probe
Hard-coded zoomFlaky pinsFixed seed bbox
No saved search ACLPrivacyUser scope probe

Example scenario

Situation: Buyer filters 2+ beds under $700k; map shows pin for listing sold yesterday.

Expected outcome: Sold listing excluded from list and map; probe search ids omit sold id.

Why UI-only automation breaks: List correct but map pin remains—user tours unavailable home.

  1. Arrange: Seed L3 sold with coords overlapping active listings.
  2. Act: Apply price and beds filters matching L1 only.
  3. Assert: Probe ids exclude L3; map pin L3 absent; detail URL for L3 returns gone or redirect.

TestChimp workflow: Instrument search_filters in prod; evolve price+beds combo if top traffic untested.

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 map and list view stay in sync?

Apply filters, GET search probe for listing ids, compare to list card data-listing-id attributes, click map pin and assert list highlights same id.

How do sold listings leave search?

Seed sold status, assert probe omits from active search and map pin test id absent—test index lag with poll if async MLS sync.

Map clustering breaks pin counts?

Do not assert pin DOM count—use probe id set as source of truth; optional cluster expand interaction if product supports.

How do draw-on-map searches work in E2E?

Simulate polygon via test API setting bounds, or evaluate map library setBounds in test env, probe geo query returns expected ids inside polygon.

Saved search privacy?

Create saved search as user A, probe user B cannot fetch A saved search id—403 or empty.

Which search_filters matter most?

TrueCoverage search_filters distribution—evolve top combos like price range + beds + property type.

Google Maps vs custom map?

Custom maps allow data-testid pins; Google embed may need probe-first with limited pin click—see maps integration guide.

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