Mocking Static Functions

edit

Mocking Static Functions in Lucee

Understanding Static Functions in Lucee

In Lucee, static functions belong to the component definition rather than a specific component instance. This means they can be called directly using the component name:

// Test.cfc
component {
    public static function myStaticFunction() {
        return "static";
    }
}

Calling the static function:

dump(Test::myStaticFunction()); // Outputs: "static"

Unlike instance functions, static functions:

  • Do not require an object instance to be called.
  • Are shared across all instances of the component.
  • Can be accessed exactly like instance functions.

Accessing Static Functions via Instances

In Lucee, a static function can also be accessed through an instance of the component, just like an instance function:

testInstance = new Test();
dump(testInstance.myStaticFunction()); // Outputs: "static"

Mocking Static Functions

One key advantage of this behavior is that static functions can be mocked just like instance functions.

Example:

testInstance = new Test();
// overlay static function with different behaviour
testInstance.myStaticFunction = function() {
    return "mockstatic";
};

dump(Test::myStaticFunction()); // Outputs: "static" dump(testInstance.myStaticFunction()); // Outputs: "mockstatic"

Why This Matters for Testing

  • There is no need to create instance wrapper functions for static functions.
  • Static functions can be dynamically mocked per instance without modifying the component.
  • This allows for cleaner test code and avoids unnecessary duplication.

Retrieving Static and Instance Function Metadata

Lucee provides a way to retrieve metadata for both static and instance functions using getMetadata. The structure of the returned metadata is identical for both, with the only difference being a static flag:

testInstance = new Test();
dump(getMetadata(testInstance).functions);

Example output:

[
    {"name": "myInstanceFunction", "static": false,...},
    {"name": "myStaticFunction", "static": true,...}
]

This means that static and instance functions are represented the same way in metadata, with the static flag indicating whether a function is static or not.

The Benefit of Static Functions

  1. No Difference in Access – Static functions work exactly like instance functions.
  2. Ease of Mocking – Static functions can be mocked at the instance level, avoiding unnecessary wrappers.
  3. Consistency – Static functions ensure a uniform implementation across instances while still allowing for instance-level customization when needed.
  4. Overlay vs. Overwrite – When an instance function is redefined (mocked), it overwrites the original implementation for that instance. With static functions, defining an instance-level function of the same name overlays the static function for that instance only, while the original static function remains accessible via the component definition.