What Is Keyword-Driven Testing?
Keyword-driven testing (also called table-driven or action-word testing) separates test design from test implementation by defining tests as sequences of keywords — human-readable action words that map to automation code.
A keyword table might look like this:
| Step | Keyword | Argument 1 | Argument 2 |
|---|---|---|---|
| 1 | Open Browser | https://app.example.com | Chrome |
| 2 | Enter Text | admin@test.com | |
| 3 | Enter Text | #password | secret123 |
| 4 | Click Button | #login-btn | |
| 5 | Verify Text | .welcome | Welcome, Admin |
| 6 | Close Browser |
Non-technical team members can read, write, and maintain these tables without understanding the underlying automation code.
Architecture
Keyword-driven testing has three layers:
┌─────────────────────┐
│ Test Cases │ Keyword tables (what to test)
│ (Keywords + Data) │
├─────────────────────┤
│ Keyword Library │ Mapping keywords to code
│ (Definitions) │
├─────────────────────┤
│ Automation Engine │ Selenium, Playwright, API clients
│ (Implementation) │
└─────────────────────┘
Layer 1: Test Cases (Keyword Tables)
Written in spreadsheets, CSV files, or specialized tools:
Test Case: Valid Login
Open Browser ${BASE_URL}
Login admin@test.com secret123
Verify Page Dashboard
Logout
Close Browser
Layer 2: Keyword Library
Definitions that map keywords to automation code:
const keywords = {
'Open Browser': async (url) => {
browser = await chromium.launch();
page = await browser.newPage();
await page.goto(url);
},
'Login': async (email, password) => {
await page.fill('#email', email);
await page.fill('#password', password);
await page.click('#login-btn');
},
'Verify Page': async (pageName) => {
const titles = { Dashboard: 'Dashboard - MyApp', Profile: 'Profile - MyApp' };
await expect(page).toHaveTitle(titles[pageName]);
},
'Logout': async () => {
await page.click('#logout');
},
'Close Browser': async () => {
await browser.close();
}
};
Layer 3: Automation Engine
The actual browser automation, API calls, and assertions handled by tools like Playwright or Selenium.
Robot Framework: The Standard
Robot Framework is the most popular keyword-driven testing tool. It uses a tabular syntax with .robot files.
Basic Robot Framework Test
*** Settings ***
Library Browser
*** Test Cases ***
Valid Login
New Browser chromium headless=false
New Page https://app.example.com/login
Fill Text #email admin@test.com
Fill Text #password secret123
Click #login-btn
Get Title == Dashboard - MyApp
Invalid Login Shows Error
New Browser chromium headless=false
New Page https://app.example.com/login
Fill Text #email wrong@test.com
Fill Text #password wrongpass
Click #login-btn
Get Text .error == Invalid credentials
Custom Keywords in Robot Framework
*** Keywords ***
Login With Credentials
[Arguments] ${email} ${password}
Fill Text #email ${email}
Fill Text #password ${password}
Click #login-btn
Verify Dashboard Loaded
Get Title == Dashboard - MyApp
Get Text .welcome contains Welcome
*** Test Cases ***
Admin Can Access Dashboard
New Browser chromium headless=false
New Page https://app.example.com/login
Login With Credentials admin@test.com secret123
Verify Dashboard Loaded
Building a Custom Keyword Engine
If Robot Framework does not fit your stack, you can build a lightweight keyword engine:
class KeywordEngine {
constructor() {
this.keywords = new Map();
this.variables = new Map();
}
register(name, implementation) {
this.keywords.set(name.toLowerCase(), implementation);
}
setVariable(name, value) {
this.variables.set(name, value);
}
resolveArgs(args) {
return args.map(arg => {
if (arg.startsWith('${') && arg.endsWith('}')) {
const varName = arg.slice(2, -1);
return this.variables.get(varName) || arg;
}
return arg;
});
}
async execute(keyword, ...args) {
const impl = this.keywords.get(keyword.toLowerCase());
if (!impl) throw new Error(`Unknown keyword: ${keyword}`);
const resolvedArgs = this.resolveArgs(args);
await impl(...resolvedArgs);
}
async runTestCase(steps) {
for (const step of steps) {
const [keyword, ...args] = step;
console.log(` Executing: ${keyword} ${args.join(' ')}`);
await this.execute(keyword, ...args);
}
}
}
Advanced Keyword Patterns
Composite Keywords
Build complex keywords from simpler ones:
engine.register('complete checkout', async (product, card) => {
await engine.execute('add to cart', product);
await engine.execute('go to checkout');
await engine.execute('enter payment', card);
await engine.execute('confirm order');
});
// Test case uses the composite keyword
const testCase = [
['login', 'customer@test.com', 'password'],
['complete checkout', 'Wireless Mouse', '4242424242424242'],
['verify order confirmation'],
];
Data-Driven Keywords
Combine keyword-driven with data-driven approaches:
Test Template: Login Test
[Arguments] ${email} ${password} ${expected}
Open Browser ${BASE_URL}
Login ${email} ${password}
Verify Result ${expected}
Test Data:
| admin@test.com | AdminPass1 | success |
| wrong@test.com | wrong | error |
| "" | "" | error |
Error Handling Keywords
engine.register('try keyword', async (keyword, ...args) => {
try {
await engine.execute(keyword, ...args);
return true;
} catch (error) {
console.log(`Keyword failed: ${keyword} - ${error.message}`);
return false;
}
});
engine.register('retry keyword', async (keyword, retries, ...args) => {
for (let i = 0; i < parseInt(retries); i++) {
try {
await engine.execute(keyword, ...args);
return;
} catch (e) {
if (i === parseInt(retries) - 1) throw e;
}
}
});
When to Use Keyword-Driven Testing
| Scenario | Keyword-Driven? |
|---|---|
| Non-technical team members write tests | Yes |
| Highly regulated industry (requires traceable test cases) | Yes |
| Small team, all programmers | Probably not |
| Complex logic requiring code flexibility | No |
| Integration with existing code-based framework | Hybrid approach |
| Rapid prototyping | No |
Advantages
- Accessible to non-programmers
- Tests serve as documentation
- Separation of test design from implementation
- Easy to create new test cases from existing keywords
Disadvantages
- Additional abstraction layer adds complexity
- Debugging can be harder (which keyword failed and why?)
- Limited flexibility compared to code-based tests
- Maintaining the keyword library requires developer support
Exercise: Design a Keyword-Driven Test Suite
Create a keyword library and test cases for an e-commerce application:
- Define 10 keywords: OpenBrowser, Login, SearchProduct, AddToCart, ViewCart, UpdateQuantity, RemoveItem, Checkout, VerifyOrderTotal, CloseBrowser
- Write 3 test cases using these keywords (happy path, empty cart, invalid payment)
- Create 2 composite keywords that combine multiple basic keywords
- Add a data-driven test case that tests 5 different product searches
- Implement the keyword definitions using Playwright
Key Takeaways
- Keyword-driven testing uses human-readable keywords to abstract automation complexity
- Three layers: test cases (keywords), keyword library (definitions), automation engine
- Robot Framework is the industry standard for keyword-driven testing
- Best suited for teams with non-technical test designers
- Composite keywords build complex tests from simple building blocks
- Can be combined with data-driven testing for maximum flexibility