Skip to main content

REUSABILITY

Code Reuse

Effective code reuse is essential for maintainable test automation. Karate provides powerful mechanisms for sharing functionality through JavaScript functions, Java integration, and reusable utilities.

JavaScript Functions

Karate allows you to define and reuse JavaScript functions throughout your tests, enabling complex data manipulation and business logic encapsulation.

Defining Functions

Feature: JavaScript function examples

Background:
# Inline function definition
* def greeter = function(name){ return 'Hello ' + name + '!' }

# Multi-line function
* def calculator =
"""
function(a, b, operation) {
if (operation == 'add') return a + b;
if (operation == 'multiply') return a * b;
if (operation == 'divide') return b != 0 ? a / b : 'Error';
return 0;
}
"""

Scenario: Use JavaScript functions
* def greeting = greeter('World')
* match greeting == 'Hello World!'

* def result = calculator(10, 5, 'add')
* match result == 15
```gherkin

### Function Libraries

Create reusable function libraries:

**utils.js:**
```javascript
function() {
return {
// Data generators
generateEmail: function() {
return 'user' + new Date().getTime() + '@example.com';
},

generatePhone: function() {
return '+1' + Math.floor(Math.random() * 9000000000 + 1000000000);
},

// Validators
isValidEmail: function(email) {
return /^[\w\.-]+@[\w\.-]+\.\w+$/.test(email);
},

// Date utilities
addDays: function(date, days) {
var result = new Date(date);
result.setDate(result.getDate() + days);
return result;
},

// API helpers
buildAuthHeader: function(token) {
return { 'Authorization': 'Bearer ' + token };
}
};
}
```gherkin

**Using function libraries:**
```gherkin
Feature: Use function library

Background:
* def utils = call read('utils.js')

Scenario: Generate test data
* def email = utils.generateEmail()
* assert utils.isValidEmail(email)

* def tomorrow = utils.addDays(new Date(), 1)
* def headers = utils.buildAuthHeader(token)
```gherkin

### Function Argument Rules

When calling JavaScript functions with `call`:

```gherkin
# Single argument - passed directly
* def result = call myFunc 'arg'

# Multiple arguments - use array
* def result = call myFunc ['arg1', 'arg2', 'arg3']

# Named arguments - use JSON
* def result = call myFunc { name: 'John', age: 30 }

# Complex example
* def processUser =
"""
function(user) {
return {
fullName: user.firstName + ' ' + user.lastName,
adult: user.age >= 18,
email: user.email.toLowerCase()
};
}
"""
* def processed = call processUser { firstName: 'John', lastName: 'Doe', age: 25, email: 'JOHN@EXAMPLE.COM' }
```gherkin

### Return Types

JavaScript functions can return various types:

```javascript
// Return primitives
function() { return 42; }
function() { return 'text'; }
function() { return true; }

// Return objects
function() {
return {
status: 'success',
data: [1, 2, 3]
};
}

// Return functions
function() {
return function(x) {
return x * 2;
};
}

// Return null/undefined
function() {
// Returns undefined
}
function() {
return null;
}
```gherkin

### Shared Scope

Variables can be shared between functions:

```gherkin
Feature: Shared scope example

Background:
# Initialize shared state
* def sharedData = {}

* def addToShared =
"""
function(key, value) {
sharedData[key] = value;
}
"""

* def getFromShared =
"""
function(key) {
return sharedData[key];
}
"""

Scenario: Use shared scope
* call addToShared ['user', 'John']
* call addToShared ['token', 'abc123']

* def user = call getFromShared 'user'
* match user == 'John'
```gherkin

## call vs read()

Understanding when to use `call` versus `read()`:

| Aspect | `call` | `read()` |
|--------|--------|----------|
| **Purpose** | Execute feature/function | Load file content |
| **Returns** | Result of execution | File content |
| **Use Case** | Run tests, invoke functions | Load data, templates |
| **Arguments** | Supports arguments | No arguments |
| **Execution** | Runs code | Just reads |
| **Variables** | Returns variables | Returns content |

### Examples

```gherkin
# call - executes and returns result
* def result = call read('auth.feature')
* def token = result.response.token

# read - loads content
* def template = read('request.json')
* def users = read('users.csv')

# call with arguments
* def result = call read('search.feature') { query: 'test' }

# read for data
* def expected = read('expected-response.json')
* match response == expected
```gherkin

## Java Integration

Karate seamlessly integrates with Java code for advanced functionality.

### Calling Java Methods

```gherkin
Feature: Java integration

Background:
# Static method call
* def uuid = java.util.UUID.randomUUID() + ''

# Using Java classes
* def SimpleDateFormat = Java.type('java.text.SimpleDateFormat')
* def formatter = new SimpleDateFormat('yyyy-MM-dd')
* def today = formatter.format(new java.util.Date())

Scenario: Use Java utilities
# String operations
* def upper = 'hello'.toUpperCase()
* match upper == 'HELLO'

# Collections
* def ArrayList = Java.type('java.util.ArrayList')
* def list = new ArrayList()
* list.add('item1')
* list.add('item2')
* match list.size() == 2
```gherkin

### Custom Java Classes

**TestHelper.java:**
```java
package com.example.utils;

public class TestHelper {
public static String encrypt(String text) {
// Encryption logic
return "encrypted_" + text;
}

public static boolean validateFormat(String data, String pattern) {
return data.matches(pattern);
}

public Map<String, Object> processData(Map<String, Object> input) {
// Processing logic
input.put("processed", true);
input.put("timestamp", System.currentTimeMillis());
return input;
}
}
```gherkin

**Using custom Java:**
```gherkin
Feature: Custom Java usage

Background:
* def TestHelper = Java.type('com.example.utils.TestHelper')
* def helper = new TestHelper()

Scenario: Use custom Java class
# Static methods
* def encrypted = TestHelper.encrypt('sensitive')
* def valid = TestHelper.validateFormat('ABC123', '[A-Z]{3}[0-9]{3}')

# Instance methods
* def input = { name: 'test', value: 100 }
* def processed = helper.processData(input)
* match processed.processed == true
```gherkin

## Commonly Needed Utilities

Build a library of common test utilities:

### Data Generation Utilities

```javascript
// data-utils.js
function() {
var utils = {
// Random generators
randomInt: function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},

randomString: function(length) {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var result = '';
for (var i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
},

// Test data builders
createUser: function(overrides) {
var defaults = {
id: utils.randomInt(1, 10000),
username: 'user_' + utils.randomString(8),
email: utils.randomString(10) + '@example.com',
active: true
};
return Object.assign(defaults, overrides || {});
},

// Bulk data generation
createUsers: function(count) {
var users = [];
for (var i = 0; i < count; i++) {
users.push(utils.createUser());
}
return users;
}
};
return utils;
}
```gherkin

### Validation Utilities

```javascript
// validation-utils.js
function() {
return {
// Format validators
isValidUUID: function(uuid) {
var pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
return pattern.test(uuid);
},

isValidDate: function(dateString, format) {
try {
var SimpleDateFormat = Java.type('java.text.SimpleDateFormat');
var formatter = new SimpleDateFormat(format || 'yyyy-MM-dd');
formatter.setLenient(false);
formatter.parse(dateString);
return true;
} catch(e) {
return false;
}
},

// Business validators
isValidResponse: function(response) {
return response.status >= 200 &&
response.status < 300 &&
response.data != null;
},

// Schema validation
validateSchema: function(object, schema) {
for (var key in schema) {
if (!(key in object)) return false;
if (typeof object[key] != schema[key]) return false;
}
return true;
}
};
}
```gherkin

### API Testing Utilities

```javascript
// api-utils.js
function() {
return {
// Retry logic
retryRequest: function(requestFunc, maxRetries, delay) {
var retries = 0;
var result;

while (retries < maxRetries) {
result = requestFunc();
if (result.status == 200) return result;

retries++;
if (retries < maxRetries) {
java.lang.Thread.sleep(delay || 1000);
}
}
return result;
},

// Response helpers
extractToken: function(response) {
if (response.headers['Authorization']) {
return response.headers['Authorization'][0].replace('Bearer ', '');
}
if (response.cookies.token) {
return response.cookies.token.value;
}
if (response.body && response.body.token) {
return response.body.token;
}
return null;
},

// Error formatting
formatError: function(response) {
return {
status: response.status,
error: response.body ? response.body.error : 'Unknown error',
timestamp: new Date().toISOString()
};
}
};
}
```gherkin

## Best Practices

### Function Organization

```gherkin
test-project/
├── features/
│ ├── users/
│ │ └── users.feature
│ └── products/
│ └── products.feature
├── utils/
│ ├── common.js
│ ├── data-generators.js
│ ├── validators.js
│ └── api-helpers.js
└── java/
└── src/test/java/
└── com/example/
└── TestHelpers.java
```gherkin

### Reusable Patterns

```gherkin
# Create reusable authentication
@ignore
Feature: Auth Helper

Scenario: Get auth token
* url authUrl
* request { username: '#(username)', password: '#(password)' }
* method post
* status 200

# Use in tests
Feature: API Test

Background:
* def auth = call read('classpath:utils/auth.feature') { username: 'test', password: 'pass' }
* def token = auth.response.token
* header Authorization = 'Bearer ' + token
```gherkin

### Error Handling

```javascript
// Safe function with error handling
function() {
return {
safeParseJson: function(text) {
try {
return JSON.parse(text);
} catch(e) {
karate.log('JSON parse error:', e.message);
return null;
}
},

safeGet: function(obj, path, defaultValue) {
try {
var parts = path.split('.');
var current = obj;
for (var i = 0; i < parts.length; i++) {
current = current[parts[i]];
if (current === undefined) return defaultValue;
}
return current;
} catch(e) {
return defaultValue;
}
}
};
}
```gherkin

## Next Steps

- Learn about [Calling Features](/docs/reusability/calling-features) for test composition
- Explore [Data-Driven Tests](/docs/reusability/data-driven-tests) for parameterized testing
- Review [Dynamic Scenarios](/docs/reusability/dynamic-scenarios) for advanced patterns