Hone logo
Hone
Problems

Jest Hook Cleanup: Ensuring Resources Are Released

Jest provides powerful lifecycle hooks like beforeAll, beforeEach, afterAll, and afterEach to manage test setup and teardown. This challenge focuses on implementing the cleanup aspect, specifically ensuring that resources initialized in setup hooks are properly released in teardown hooks. This is crucial for preventing resource leaks and ensuring test isolation.

Problem Description

You need to implement a Jest test suite for a hypothetical Resource class. This Resource class has initialize and release methods. The initialize method should be called before each test (beforeEach) to set up a new instance of the resource. The release method should be called after each test (afterEach) to clean up the initialized resource.

Your task is to write the Jest tests that verify the correct execution of initialize and release methods. You should ensure that initialize is called exactly once before each test and release is called exactly once after each test, and that they operate on the same resource instance.

Key Requirements:

  1. Resource Class Simulation: Create a mock or simulated Resource class with initialize and release methods. These methods should track whether they have been called and which resource instance they are operating on.
  2. Jest Hooks Implementation: Utilize beforeEach and afterEach hooks in your Jest test suite.
  3. Verification: Write assertions to verify:
    • That initialize is called before each test begins.
    • That release is called after each test completes.
    • That the same Resource instance is passed to both initialize and release within a single test's lifecycle.
    • That no resource is left unreleased after all tests have completed (if applicable, consider a scenario where a global resource might be initialized in beforeAll and released in afterAll).

Expected Behavior:

For a test suite with N tests, initialize should be called N times, and release should be called N times. Each call to initialize should be followed by its corresponding test execution, and then by a call to release with the same resource instance.

Examples

Example 1:

Consider a simple test suite with one test:

// Hypothetical Resource class (you'll simulate this or use a mock)
class Resource {
  public id: string;
  private initialized: boolean = false;
  private released: boolean = false;

  constructor(id: string) {
    this.id = id;
  }

  initialize(): void {
    console.log(`Initializing resource ${this.id}`);
    this.initialized = true;
  }

  release(): void {
    console.log(`Releasing resource ${this.id}`);
    this.released = true;
  }

  isInitialized(): boolean { return this.initialized; }
  isReleased(): boolean { return this.released; }
}

// --- Jest Test Suite ---

describe('Resource Management', () => {
  let myResource: Resource | null = null;

  beforeEach(() => {
    // Setup: Initialize a new resource before each test
    myResource = new Resource('test-resource-id');
    myResource.initialize();
  });

  afterEach(() => {
    // Teardown: Release the resource after each test
    if (myResource) {
      myResource.release();
      myResource = null; // Ensure it's reset for the next test
    }
  });

  test('should initialize and release a resource', () => {
    // This test will run with a fresh resource
    expect(myResource).not.toBeNull();
    expect(myResource!.isInitialized()).toBe(true);
    // The release will happen in afterEach
  });
});

Expected Output (Console Logs):

Initializing resource test-resource-id
Releasing resource test-resource-id

Explanation:

The beforeEach hook creates a Resource instance and calls initialize. The test then executes. Finally, the afterEach hook calls release on the same Resource instance.

Example 2: Multiple Tests

Consider a test suite with two tests.

// Using the same hypothetical Resource class as Example 1

// --- Jest Test Suite ---
describe('Resource Management with Multiple Tests', () => {
  let myResource: Resource | null = null;

  beforeEach(() => {
    myResource = new Resource(`resource-${Math.random().toString(36).substring(7)}`);
    myResource.initialize();
  });

  afterEach(() => {
    if (myResource) {
      myResource.release();
      myResource = null;
    }
  });

  test('test 1: resource is initialized', () => {
    expect(myResource).not.toBeNull();
    expect(myResource!.isInitialized()).toBe(true);
    expect(myResource!.isReleased()).toBe(false); // Should not be released yet
  });

  test('test 2: resource is initialized and ready', () => {
    expect(myResource).not.toBeNull();
    expect(myResource!.isInitialized()).toBe(true);
    expect(myResource!.isReleased()).toBe(false); // Should not be released yet
  });
});

Expected Output (Console Logs - order may vary slightly for IDs):

Initializing resource <random_id_1>
Releasing resource <random_id_1>
Initializing resource <random_id_2>
Releasing resource <random_id_2>

Explanation:

Each test gets its own independent Resource instance. beforeEach sets it up, the test runs, and afterEach tears it down.

Constraints

  • The simulated Resource class should be a simple class within your test file or imported from a separate mock module.
  • The test suite should use standard Jest describe, test, beforeEach, and afterEach functions.
  • You must use TypeScript for your solution.
  • Avoid using any external libraries specifically designed for mocking lifecycle hooks; rely on Jest's built-in capabilities.

Notes

  • Think about how to best track whether initialize and release are called, and which instance they operate on. You might use mock functions or add internal state to your simulated Resource class.
  • Consider the scenario where a resource might be initialized once for the entire suite (beforeAll) and released once at the end (afterAll). While the primary focus is beforeEach/afterEach, understanding beforeAll/afterAll is relevant to resource management.
  • The goal is not to test the logic of the Resource class itself, but rather to test that your Jest hooks are correctly orchestrating its lifecycle.
  • Success is defined by passing all Jest assertions that verify the correct invocation and state of the initialize and release methods.
Loading editor...
typescript