Function Wrappers

Introduction

A Function Wrapper is one of the simplest and most common implementations of the Decorator Pattern in JavaScript. Instead of modifying an existing function, a wrapper function surrounds the original function and adds extra behavior before, after, or around its execution.

A function wrapper receives the original function as an argument, performs additional operations such as logging, validation, authentication, timing, or error handling, and then calls the original function.

Because JavaScript treats functions as first-class objects, functions can be passed as arguments, returned from other functions, and stored in variables. This makes function wrappers easy to implement and highly reusable.

In Node.js, function wrappers are widely used in Express middleware, logging libraries, authentication systems, caching, monitoring, and automation frameworks.

For automation engineers, function wrappers are extremely useful for adding screenshots, retries, logging, reporting, timing, and validation without modifying the original automation code.

In this tutorial, you’ll learn how function wrappers work and how to use them effectively.


What is a Function Wrapper?

A Function Wrapper is a function that accepts another function, adds additional functionality, and then executes the original function.

The original function remains unchanged while the wrapper enhances its behavior.


Why Use Function Wrappers?

Function wrappers help developers:

  • Reuse common functionality.

  • Avoid duplicate code.

  • Improve code maintainability.

  • Add features dynamically.

  • Separate business logic from utility logic.

  • Follow the Decorator Pattern.

  • Keep code modular.


Basic Structure

Original Function

        ↓

Function Wrapper

        ↓

Enhanced Function

Example 1: Basic Function Wrapper

function greet() {

    console.log(

        "Hello!"

    );

}

function wrapper(func) {

    return function () {

        console.log(

            "Before execution"

        );

        func();

        console.log(

            "After execution"

        );

    };

}

const wrappedGreet =

    wrapper(greet);

wrappedGreet();

Sample Output

Before execution
Hello!
After execution

Example 2: Logging Wrapper

function login() {

    console.log(

        "User logged in."

    );

}

function logger(func) {

    return function () {

        console.log(

            "Logging started."

        );

        func();

        console.log(

            "Logging completed."

        );

    };

}

const wrappedLogin =

    logger(login);

wrappedLogin();

Sample Output

Logging started.
User logged in.
Logging completed.

Example 3: Timing Wrapper

function processData() {

    console.log(

        "Processing..."

    );

}

function timer(func) {

    return function () {

        console.time(

            "Execution"

        );

        func();

        console.timeEnd(

            "Execution"

        );

    };

}

const wrappedProcess =

    timer(processData);

wrappedProcess();

Sample Output

Processing...
Execution: <execution time>

Example 4: Validation Wrapper

function register(name) {

    console.log(

        "Welcome",

        name

    );

}

function validate(func) {

    return function (name) {

        if (!name) {

            console.log(

                "Name is required."

            );

            return;

        }

        func(name);

    };

}

const wrappedRegister =

    validate(register);

wrappedRegister("Rahul");

wrappedRegister("");

Sample Output

Welcome Rahul
Name is required.

Example 5: Error Handling Wrapper

function divide(a, b) {

    if (b === 0) {

        throw new Error(

            "Division by zero."

        );

    }

    console.log(a / b);

}

function safeExecute(func) {

    return function (a, b) {

        try {

            func(a, b);

        }

        catch (error) {

            console.log(

                error.message

            );

        }

    };

}

const wrappedDivide =

    safeExecute(divide);

wrappedDivide(10, 2);

wrappedDivide(10, 0);

Sample Output

5
Division by zero.

Automation Testing Examples

Function wrappers are widely used to extend automation scripts.

Playwright Example

Add browser launch logging.

function launchBrowser() {

    console.log(

        "Browser launched."

    );

}

function logger(func) {

    return function () {

        console.log(

            "Launching browser..."

        );

        func();

    };

}

logger(launchBrowser)();

Sample Output

Launching browser...
Browser launched.

Selenium Example

Add retry information.

function executeTest() {

    console.log(

        "Test executed."

    );

}

function retry(func) {

    return function () {

        console.log(

            "Retry enabled."

        );

        func();

    };

}

retry(executeTest)();

Sample Output

Retry enabled.
Test executed.

Cypress Example

Capture screenshots automatically.

function verifyPage() {

    console.log(

        "Page verified."

    );

}

function screenshot(func) {

    return function () {

        func();

        console.log(

            "Screenshot captured."

        );

    };

}

screenshot(verifyPage)();

Sample Output

Page verified.
Screenshot captured.

API Testing Example

Log API execution time.

function callApi() {

    console.log(

        "API request sent."

    );

}

timer(callApi)();

Sample Output

API request sent.
Execution: <execution time>

Data-Driven Testing Example

Validate test case IDs.

function executeTest(testCase) {

    console.log(

        "Executing",

        testCase

    );

}

const validateTest =

    validate(executeTest);

validateTest("TC001");

validateTest("");

Sample Output

Executing TC001
Name is required.

Real-World Uses of Function Wrappers

Function wrappers are commonly used for:

  • Logging.

  • Authentication.

  • Authorization.

  • Validation.

  • Error handling.

  • Retry mechanisms.

  • Performance monitoring.

  • Screenshot capture.

  • Caching.

  • Test reporting.


Benefits of Function Wrappers

  • Improve code reuse.

  • Keep original functions unchanged.

  • Add functionality dynamically.

  • Simplify maintenance.

  • Reduce duplicate code.

  • Improve application flexibility.

  • Support modular programming.


Common Mistakes

Modifying the Original Function

A wrapper should extend the original function instead of changing its implementation.


Forgetting to Return the Wrapper Function

Always return the new wrapped function from the wrapper.


Mixing Multiple Responsibilities

Each wrapper should perform one specific task such as logging, validation, or timing.


Best Practices

  • Keep wrappers focused on one responsibility.

  • Use descriptive wrapper names.

  • Preserve the original function’s behavior.

  • Reuse wrappers wherever possible.

  • Combine multiple wrappers when needed.

  • Test wrapper functions independently.

  • Keep wrapper code simple and readable.


Conclusion

Function wrappers are one of the simplest and most effective ways to implement the Decorator Pattern in JavaScript. They allow developers to enhance existing functions without modifying the original implementation, leading to cleaner, more maintainable, and reusable code.

For automation engineers, function wrappers are invaluable for adding reusable capabilities such as logging, retries, validation, screenshots, reporting, authentication, and performance monitoring. By separating these cross-cutting concerns from the main automation logic, scripts become easier to manage, test, and extend.


Frequently Asked Questions (FAQs)

What is a function wrapper?

A function wrapper is a function that surrounds another function and adds extra behavior before, after, or around its execution.


Why use function wrappers?

They allow developers to extend functionality without modifying the original function.


Are function wrappers the same as decorators?

Function wrappers are a common JavaScript implementation of the Decorator Pattern.


Can multiple wrappers be combined?

Yes. Multiple wrappers can be layered to add different behaviors such as logging, validation, and timing.


Why are function wrappers useful in automation testing?

They help add reusable features like retries, logging, screenshots, reporting, and validation without changing the original test scripts.


Key Takeaways

  • Function wrappers implement the Decorator Pattern in JavaScript.

  • They extend functions without modifying their original code.

  • JavaScript functions are first-class objects, making wrappers easy to implement.

  • Wrappers improve code reuse and maintainability.

  • Common uses include logging, validation, authentication, caching, and error handling.

  • Automation frameworks use wrappers for retries, screenshots, reporting, and timing.

  • Each wrapper should have a single responsibility.

  • Multiple wrappers can be combined to create layered functionality.

  • Wrappers help keep business logic clean and modular.

  • Mastering function wrappers is essential for writing scalable and maintainable Node.js applications.