Hone logo
Hone
Problems

Unit Testing a Simple Cache with Jest

This challenge focuses on writing unit tests for a basic in-memory cache implementation using Jest in TypeScript. Writing robust tests is crucial for ensuring the reliability and correctness of any software component, and a cache is a common building block in many applications.

Problem Description

You are tasked with creating a comprehensive suite of unit tests for a simple Cache class. This class should support basic operations like set (to add or update a key-value pair), get (to retrieve a value by key), and delete (to remove a key-value pair). The cache should store data in memory and should also support an optional expiration time for entries.

Key Requirements:

  • set(key: string, value: any, ttl?: number): void:
    • Adds a new key-value pair to the cache.
    • If the key already exists, its value and expiration time should be updated.
    • ttl (time-to-live) is an optional parameter in milliseconds. If provided, the entry should expire after this duration.
  • get(key: string): any | undefined:
    • Retrieves the value associated with the given key.
    • If the key does not exist or has expired, it should return undefined.
  • delete(key: string): boolean:
    • Removes the key-value pair from the cache.
    • Returns true if the key was found and deleted, false otherwise.
  • Expiration Handling: Entries with a ttl should be automatically removed from the cache once they expire.

Expected Behavior:

  • Successfully setting and getting a value should return the correct value.
  • Attempting to get a non-existent key should return undefined.
  • Deleting an existing key should remove it and return true.
  • Deleting a non-existent key should return false.
  • Expired entries should not be retrievable via get.

Edge Cases to Consider:

  • Setting and getting values of different data types.
  • Setting a ttl of 0 or a very small ttl.
  • Interactions between set, get, and delete operations.
  • Handling concurrent operations (though for this in-memory cache, this is less critical for basic unit tests, but good to keep in mind).

Examples

Example 1: Basic Set and Get

// Assume Cache class is implemented and imported
const cache = new Cache();
cache.set('user:1', { name: 'Alice' });
const user = cache.get('user:1'); // user should be { name: 'Alice' }

Explanation: A simple key-value pair is added and then retrieved successfully.

Example 2: Update and Delete

const cache = new Cache();
cache.set('count', 10);
console.log(cache.get('count')); // Output: 10

cache.set('count', 20); // Update the value
console.log(cache.get('count')); // Output: 20

const deleted = cache.delete('count'); // deleted should be true
console.log(deleted); // Output: true
console.log(cache.get('count')); // Output: undefined

Explanation: The value for 'count' is updated, and then the key is successfully deleted.

Example 3: Expiration

// Use Jest's fake timers for precise time control
jest.useFakeTimers();

const cache = new Cache();
cache.set('temp_data', 'sensitive_info', 500); // Expires in 500ms

console.log(cache.get('temp_data')); // Output: 'sensitive_info'

// Advance timers by less than the TTL
jest.advanceTimersByTime(400);
console.log(cache.get('temp_data')); // Output: 'sensitive_info'

// Advance timers beyond the TTL
jest.advanceTimersByTime(200); // Total advance: 600ms
console.log(cache.get('temp_data')); // Output: undefined

Explanation: An entry with a TTL is set. It remains accessible until the TTL passes, after which it becomes undefined.

Constraints

  • The Cache class will be implemented in TypeScript.
  • Your tests should be written using Jest.
  • The Cache class should not rely on external libraries for its core functionality (e.g., no external caching libraries).
  • Your tests should cover all the key requirements and edge cases mentioned above.
  • Ensure tests are efficient and do not rely on excessively long waits (use fake timers for time-based tests).

Notes

  • For tests involving expiration, leverage Jest's useFakeTimers() and advanceTimersByTime() to simulate the passage of time without actually waiting. Remember to clean up fake timers in your test setup/teardown.
  • Consider using jest.spyOn() to mock or observe specific methods if needed, though for this straightforward implementation, direct assertions should suffice for most cases.
  • Think about how you will structure your tests for clarity and maintainability. Group related tests using describe blocks.
Loading editor...
typescript