Introduction
The Decorator Pattern is widely used in automation frameworks to add extra functionality to existing functions without modifying their original implementation. This is commonly achieved using function wrappers, where a new function surrounds the original function and performs additional tasks before or after it executes.
In automation testing, many features such as logging, screenshots, retries, execution timing, authentication, validation, reporting, and exception handling are implemented using the Decorator Pattern.
Instead of writing the same code repeatedly in every test case, decorators allow you to reuse these common behaviors across multiple automation scripts.
Modern Node.js automation frameworks such as Playwright, Puppeteer, WebdriverIO, and custom automation frameworks frequently use this design pattern to create clean, reusable, and maintainable test code.
In this tutorial, you’ll explore practical automation examples of the Decorator Pattern.
Why Use the Decorator Pattern in Automation?
The Decorator Pattern helps automation engineers:
Add reusable functionality.
Avoid duplicate code.
Improve test maintainability.
Separate utility logic from test logic.
Simplify debugging.
Improve reporting.
Follow clean coding practices.
Example 1: Logging Test Execution
Add logging before and after a test runs.
function executeTest() {
console.log(
"Executing login test."
);
}
function logger(func) {
return function () {
console.log(
"Test started."
);
func();
console.log(
"Test completed."
);
};
}
const test =
logger(executeTest);
test();
Sample Output
Test started.
Executing login test.
Test completed.
Example 2: Browser Launch Wrapper
Add logging during browser launch.
function launchBrowser() {
console.log(
"Browser launched."
);
}
function browserLogger(func) {
return function () {
console.log(
"Launching browser..."
);
func();
};
}
browserLogger(
launchBrowser
)();
Sample Output
Launching browser...
Browser launched.
Example 3: Screenshot Decorator
Automatically capture a screenshot after verification.
function verifyHomePage() {
console.log(
"Home page verified."
);
}
function screenshot(func) {
return function () {
func();
console.log(
"Screenshot captured."
);
};
}
screenshot(
verifyHomePage
)();
Sample Output
Home page verified.
Screenshot captured.
Example 4: Retry Decorator
Automatically retry a failed operation.
function executeApiTest() {
console.log(
"API request executed."
);
}
function retry(func) {
return function () {
console.log(
"Retry mechanism enabled."
);
func();
};
}
retry(
executeApiTest
)();
Sample Output
Retry mechanism enabled.
API request executed.
Example 5: Execution Timer
Measure execution time.
function generateReport() {
console.log(
"Generating report."
);
}
function timer(func) {
return function () {
console.time(
"Execution"
);
func();
console.timeEnd(
"Execution"
);
};
}
timer(
generateReport
)();
Sample Output
Generating report.
Execution: <execution time>
Playwright Automation Example
Add logging before every browser action.
function clickLogin() {
console.log(
"Clicked Login button."
);
}
function logger(func) {
return function () {
console.log(
"Performing browser action..."
);
func();
};
}
logger(
clickLogin
)();
Sample Output
Performing browser action...
Clicked Login button.
Selenium Automation Example
Automatically capture a screenshot after test execution.
function runTest() {
console.log(
"Selenium test completed."
);
}
function screenshot(func) {
return function () {
func();
console.log(
"Screenshot saved."
);
};
}
screenshot(
runTest
)();
Sample Output
Selenium test completed.
Screenshot saved.
Cypress Automation Example
Measure page verification time.
function verifyDashboard() {
console.log(
"Dashboard verified."
);
}
timer(
verifyDashboard
)();
Sample Output
Dashboard verified.
Execution: <execution time>
API Testing Example
Validate API responses before processing.
function processResponse() {
console.log(
"Response processed."
);
}
function validate(func) {
return function () {
console.log(
"Response validated."
);
func();
};
}
validate(
processResponse
)();
Sample Output
Response validated.
Response processed.
Data-Driven Testing Example
Validate test data before execution.
function executeTest(data) {
console.log(
"Executing",
data
);
}
function validate(func) {
return function (data) {
if (!data) {
console.log(
"Invalid test data."
);
return;
}
func(data);
};
}
const run =
validate(executeTest);
run("TC001");
run("");
Sample Output
Executing TC001
Invalid test data.
Real-World Automation Uses
The Decorator Pattern is commonly used for:
Browser launch logging.
Screenshot capture.
Retry mechanisms.
Test reporting.
Authentication.
Authorization.
API validation.
Performance monitoring.
Error handling.
Data validation.
Benefits in Automation Frameworks
Improves code reuse.
Keeps test scripts clean.
Reduces duplicate code.
Makes maintenance easier.
Separates utility logic from business logic.
Simplifies debugging.
Supports modular automation architecture.
Common Mistakes
Modifying the Original Test Function
Always wrap the original function instead of editing it directly.
Combining Too Many Responsibilities
Each decorator should focus on one specific enhancement.
Forgetting to Return the Wrapped Function
Decorator functions should always return the enhanced function.
Best Practices
Keep decorators reusable.
Give wrappers meaningful names.
Add only one responsibility per decorator.
Combine decorators when necessary.
Keep test logic independent from utility logic.
Test decorators separately.
Reuse decorators across automation projects.
Conclusion
The Decorator Pattern is an excellent way to extend automation scripts without modifying their original implementation. By wrapping functions with reusable enhancements such as logging, retries, screenshots, validation, and timing, automation engineers can create cleaner and more maintainable test frameworks.
Whether you’re building browser automation with Playwright, web testing with Selenium, end-to-end tests with Cypress, or API testing in Node.js, decorators help reduce duplicate code while improving flexibility and scalability. Mastering this pattern is an important step toward designing professional automation frameworks.
Frequently Asked Questions (FAQs)
Why is the Decorator Pattern useful in automation?
It allows reusable features like logging, screenshots, retries, and reporting to be added without changing existing test code.
What is a function wrapper?
A function wrapper is a function that surrounds another function and adds extra behavior before or after its execution.
Can multiple decorators be combined?
Yes. Multiple decorators can be layered together to provide several enhancements.
Is the original function modified?
No. The original function remains unchanged, and the decorator extends its behavior.
Which automation frameworks benefit from decorators?
Playwright, Selenium, Cypress, Puppeteer, WebdriverIO, and custom Node.js automation frameworks commonly use the Decorator Pattern.
Key Takeaways
The Decorator Pattern extends functionality without modifying existing code.
Function wrappers are the most common JavaScript implementation of decorators.
Decorators improve code reuse and maintainability.
Automation frameworks use decorators for logging, retries, screenshots, reporting, and validation.
Each decorator should have a single responsibility.
Decorators keep test logic separate from utility logic.
Multiple decorators can be combined to build powerful automation workflows.
The Decorator Pattern supports scalable and modular automation frameworks.
Wrappers make automation scripts cleaner and easier to maintain.
Mastering the Decorator Pattern is valuable for professional Node.js and automation development.
