Mocking Static Functions
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
- No Difference in Access – Static functions work exactly like instance functions.
- Ease of Mocking – Static functions can be mocked at the instance level, avoiding unnecessary wrappers.
- Consistency – Static functions ensure a uniform implementation across instances while still allowing for instance-level customization when needed.
- 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.
Conclusion
Lucee’s handling of static functions provides a powerful way to structure shared functionality while maintaining flexibility in testing. Instead of introducing redundant instance function wrappers, developers can take advantage of the fact that static functions are accessible like instance functions and can be dynamically mocked per instance. Additionally, getMetadata
provides an easy way to differentiate between static and instance functions, reinforcing the consistent handling of both in Lucee.