Hone logo
Hone
Problems

Testing Decorators with Jest in TypeScript

Decorators are a powerful feature in TypeScript that allow you to add annotations and modifications to class declarations, methods, properties, and parameters. Testing these decorators ensures they behave as expected and don't introduce unintended side effects. This challenge focuses on writing robust Jest tests for custom TypeScript decorators.

Problem Description

Your task is to create a set of Jest tests for a given TypeScript decorator. You will be provided with a sample decorator and a class that uses it. Your goal is to write comprehensive tests that verify the decorator's functionality, including its intended side effects and how it interacts with the decorated code.

Key Requirements:

  • Write Jest tests in TypeScript.
  • Test the decorator's effect on the decorated class, method, or property.
  • Verify any state changes or modifications introduced by the decorator.
  • Consider different scenarios, including how the decorator behaves when the decorated element is called or accessed.

Expected Behavior:

The tests should pass if the decorator functions exactly as intended, modifying or annotating the code as designed. If the decorator has side effects (e.g., logging, modifying arguments), these should be asserted in the tests.

Edge Cases:

  • Consider what happens if the decorator is applied to different types of class members (methods, properties, constructor).
  • If the decorator takes arguments, ensure these are tested.
  • Test scenarios where the decorated method/property is called multiple times.

Examples

Let's assume a simple LogMethodCall decorator that logs when a method is called.

Example 1: Basic Method Call Logging

// Sample Decorator (provided)
function LogMethodCall(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
        console.log(`Entering method: ${propertyKey} with arguments: ${JSON.stringify(args)}`);
        const result = originalMethod.apply(this, args);
        console.log(`Exiting method: ${propertyKey} with result: ${JSON.stringify(result)}`);
        return result;
    };

    return descriptor;
}

// Sample Class (provided)
class Calculator {
    @LogMethodCall
    add(a: number, b: number): number {
        return a + b;
    }

    @LogMethodCall
    subtract(a: number, b: number): number {
        return a - b;
    }
}

Test Scenario:

Write a test to ensure that calling calculator.add(2, 3) results in appropriate console logs being output.

Expected Test Output (assertions):

The test should assert that console.log was called with specific messages indicating the method entry and exit, along with the correct arguments and return value.

Example 2: Decorator with Arguments

// Sample Decorator (provided)
function WithPrefix(prefix: string) {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;

        descriptor.value = function(...args: any[]) {
            console.log(`${prefix}: Calling ${propertyKey}`);
            const result = originalMethod.apply(this, args);
            console.log(`${prefix}: Finished ${propertyKey}`);
            return result;
        };

        return descriptor;
    };
}

// Sample Class (provided)
class Greeter {
    @WithPrefix("INFO")
    greet(name: string): string {
        return `Hello, ${name}!`;
    }
}

Test Scenario:

Write a test to verify that calling greeter.greet("World") correctly logs messages with the "INFO" prefix.

Expected Test Output (assertions):

The test should assert that console.log was called with messages starting with "INFO:" and containing the method name.

Constraints

  • The solution must be written in TypeScript.
  • Jest must be used for testing.
  • You will be provided with the decorator and the class definition. Your task is to write the .test.ts file.
  • Assume all necessary type information is available.
  • Focus on testing the behavior of the decorator, not its internal implementation details (unless those details directly affect observable behavior).

Notes

  • You'll likely need to mock console.log to capture the output from the decorators.
  • Consider using Jest's spy functions (jest.spyOn) for this purpose.
  • Think about how to instantiate the class and call the decorated methods to trigger the decorator's logic.
  • When testing decorators that modify method descriptors, remember that descriptor.value is the function that gets executed.
Loading editor...
typescript