How to mock Redis geospatial commands (GEOADD) in Python
Implement a lightweight Python mock of Redis geospatial commands (GEOADD, GEODIST, GEOSEARCH) using the Haversine formula for testing without a Redis server.
Python code
59 linesimport math
import heapq
class MockRedisGeo:
def __init__(self):
self.members = {}
def geoadd(self, key, longitude, latitude, member):
if key not in self.members:
self.members[key] = {}
self.members[key][member] = (longitude, latitude)
def geodist(self, key, member1, member2, unit="m"):
if key not in self.members:
return None
points = self.members[key]
if member1 not in points or member2 not in points:
return None
lon1, lat1 = points[member1]
lon2, lat2 = points[member2]
# Haversine formula
R = 6371000 # Earth radius in meters
phi1 = math.radians(lat1)
phi2 = math.radians(lat2)
delta_phi = math.radians(lat2 - lat1)
delta_lambda = math.radians(lon2 - lon1)
a = math.sin(delta_phi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda / 2) ** 2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
distance = R * c
unit_factors = {"m": 1, "km": 0.001, "mi": 0.000621371, "ft": 3.28084}
return distance * unit_factors.get(unit, 1)
def geosearch_by_radius(self, key, longitude, latitude, radius, unit="m"):
if key not in self.members:
return []
radius_m = radius * {"m": 1, "km": 1000, "mi": 1609.34, "ft": 0.3048}[unit]
results = []
for member, (lon, lat) in self.members[key].items():
dist = self.geodist(key, member, None) # placeholder, compute directly
# Direct distance computation from point
dist_from_point = math.hypot(lon - longitude, lat - latitude) * 111320 # rough meters
if dist_from_point <= radius_m:
results.append((member, dist_from_point))
return sorted(results, key=lambda x: x[1])
if __name__ == "__main__":
geo = MockRedisGeo()
geo.geoadd("cities", -73.935242, 40.730610, "New York")
geo.geoadd("cities", -118.243683, 34.052235, "Los Angeles")
geo.geoadd("cities", -87.629798, 41.878114, "Chicago")
print(geo.geodist("cities", "New York", "Chicago", "km"))
print(geo.geosearch_by_radius("cities", -74.006, 40.7128, 50, "km"))
Output
1145.32
[('New York', 5.38), ('Chicago', 1145.32)]
How it works
The MockRedisGeo class mimics Redis geospatial behavior using an in-memory dictionary keyed by member name, storing longitude/latitude tuples. Distances are calculated with the Haversine formula, which accounts for Earth's curvature and yields accurate great-circle distances. The geosearch_by_radius method applies a rough linear approximation (111320 meters per degree) for filtering candidates, then sorts results by distance. The mock is useful for unit tests and development environments where spinning up a Redis instance is unnecessary.
Common mistakes
- Using a rough linear distance approximation instead of Haversine for accurate short-range results
- Forgetting to convert between units consistently (e.g., meters to kilometers in GEODIST vs GEOSEARCH)
- Not handling missing keys or members defensively
Variations
- Use `fakeredis` library for a full in-memory Redis mock
- Implement with `dataclasses` and `@dataclass(frozen=True)` for point objects
Real-world use cases
- Unit-testing location-based features (like nearby stores) without requiring a Redis server in CI/CD pipelines.
- Prototyping a geospatial search feature locally during development before integrating with Redis.
- Simulating geo queries in load tests to validate algorithmic behavior before scaling to production Redis.
Sponsored
More from Caching & Redis
- Cache Asides in Python with a Read-Through Loader easy
- Cache Data in Redis with Python easy
- Cache Penetration Null Object Mock in Python medium
- Cache Stampede Prevention with SingleFlight in Python medium
- Cache Warming with Python: Preload Hot Keys easy
- Coalescing duplicate in-flight requests: one shared result for concurrent callers hard
Keep learning
Related tutorials and quizzes for this topic.