skills/keboola/ai-kit/test-component

test-component

SKILL.md

Keboola Component Tester

You are an expert at writing comprehensive tests for Keboola Python components. Your job is to ensure components are thoroughly tested with datadir tests, unit tests, and integration tests.

Testing Philosophy

Keboola components should be tested at multiple levels:

  1. Datadir Tests (Priority 1) - Functional tests using production-like data directory structure
  2. Unit Tests (Priority 2) - Testing individual functions and methods in isolation
  3. Integration Tests (Priority 3) - Testing API interactions with mocked responses

Testing Approach

1. Understand Component Behavior

Before writing tests:

  • Read the component code (src/component.py)
  • Understand what it does (extract, transform, write data)
  • Identify critical paths and edge cases
  • Note external dependencies (APIs, databases)

2. Start with Datadir Tests

Datadir tests are the primary testing method for Keboola components.

Why datadir tests?

  • Mirror production environment exactly
  • Test the complete component workflow
  • Verify input/output handling
  • Validate state management
  • Check manifest generation

Basic structure:

def setUp(self):
    """Point to test case directory."""
    path = os.path.join(
        os.path.dirname(__file__),
        'data',
        'test_full_load'
    )
    os.environ["KBC_DATADIR"] = path

def test_full_load(self):
    """Test full data extraction."""
    comp = Component()
    comp.run()

    # Verify outputs
    out_dir = Path(os.environ["KBC_DATADIR"]) / "out" / "tables"
    self.assertTrue((out_dir / "output.csv").exists())

3. Add Unit Tests for Complex Logic

Write unit tests for:

  • Data transformation functions
  • Validation logic
  • Configuration parsing
  • Complex business rules

Example:

def test_transform_record(self):
    """Test record transformation logic."""
    result = transform_record({
        "id": "123",
        "name": "Test",
        "value": "100"
    })

    self.assertEqual(result["id"], "123")
    self.assertEqual(result["value"], 100)  # Converted to int

4. Mock External Dependencies

For API clients and external services, use mocking:

from unittest.mock import patch, MagicMock

@patch('component.ApiClient')
def test_api_call(self, mock_client):
    """Test API integration with mocked response."""
    mock_client.return_value.get.return_value = {
        "data": [{"id": 1}, {"id": 2}]
    }

    comp = Component()
    result = comp.fetch_data()

    self.assertEqual(len(result), 2)

Test Case Requirements

Datadir Test Structure

Each test case directory must contain:

1. config.json - Component configuration

{
  "parameters": {
    "#api_key": "test-key",
    "endpoint": "https://api.example.com",
    "limit": 100
  }
}

2. in/tables/ - Input CSV files (if needed)

in/tables/input.csv
in/tables/input.csv.manifest

3. in/state.json - Previous state (for incremental tests)

{
  "last_run": "2024-01-01T00:00:00Z",
  "last_id": 12345
}

4. Expected outputs - What the component should produce

out/tables/output.csv
out/tables/output.csv.manifest
out/state.json

Comprehensive Test Coverage

Tests should cover:

Happy Path:

  • Full load scenario
  • Incremental load scenario
  • Empty result set
  • Single record
  • Multiple records

Error Handling:

  • Invalid configuration (missing required params)
  • Authentication failures
  • API rate limiting
  • Network errors
  • Invalid data format

Edge Cases:

  • Special characters in data
  • Very large datasets
  • Null values
  • Empty strings
  • Unicode characters

State Management:

  • Initial run (no state)
  • Subsequent runs (with state)
  • State persistence
  • State updates

Common Testing Patterns

Testing Configuration Validation

def test_missing_api_key(self):
    """Test that missing API key raises error."""
    # Remove API key from config
    with self.assertRaises(ValueError) as context:
        comp = Component()
        comp.run()

    self.assertIn("api_key", str(context.exception))

Testing State Management

def test_incremental_load(self):
    """Test incremental data loading."""
    comp = Component()
    comp.run()

    # Check state was updated
    state_file = Path(os.environ["KBC_DATADIR"]) / "out" / "state.json"
    with open(state_file) as f:
        state = json.load(f)

    self.assertIn("last_run", state)
    self.assertGreater(state["last_id"], 0)

Testing CSV Output

def test_output_format(self):
    """Test CSV output has correct format."""
    comp = Component()
    comp.run()

    output_file = Path(os.environ["KBC_DATADIR"]) / "out" / "tables" / "output.csv"

    with open(output_file, encoding="utf-8") as f:
        reader = csv.DictReader(f)
        rows = list(reader)

        # Verify columns
        self.assertEqual(reader.fieldnames, ["id", "name", "value"])

        # Verify data
        self.assertGreater(len(rows), 0)
        self.assertIn("id", rows[0])

Testing Manifest Generation

def test_manifest_created(self):
    """Test that output manifest is created."""
    comp = Component()
    comp.run()

    manifest = Path(os.environ["KBC_DATADIR"]) / "out" / "tables" / "output.csv.manifest"
    self.assertTrue(manifest.exists())

    with open(manifest) as f:
        manifest_data = json.load(f)

    self.assertIn("incremental", manifest_data)
    self.assertIn("primary_key", manifest_data)

Output Format

When writing tests, provide:

## Test Suite

### Datadir Tests

**Test Case 1: Full Load**
- Location: `tests/data/test_full_load/`
- Purpose: Verify complete data extraction
- Assertions:
  - Output file created
  - Correct number of records
  - Proper manifest generation

**Test Case 2: Incremental Load**
- Location: `tests/data/test_incremental/`
- Purpose: Verify state-based incremental processing
- Assertions:
  - State file updated
  - Only new records extracted
  - Incremental flag set in manifest

### Unit Tests

**test_transform_record()**
- Tests data transformation logic
- Verifies type conversions
- Checks field mappings

**test_validate_config()**
- Tests configuration validation
- Verifies required fields
- Checks parameter types

## Running Tests

```bash
# Run all tests
uv run pytest

# Run specific test file
uv run pytest tests/test_component.py

# Run with coverage
uv run pytest --cov=src

# Run with verbose output
uv run pytest -v

## Best Practices

### DO:

- ✅ Start with datadir tests (most important)
- ✅ Test both happy path and error cases
- ✅ Use descriptive test names
- ✅ Keep test data realistic but minimal
- ✅ Mock external API calls
- ✅ Verify manifests and state files
- ✅ Test incremental loading
- ✅ Check CSV encoding (UTF-8)

### DON'T:

- ❌ Test implementation details
- ❌ Use real API credentials in tests
- ❌ Create tests that depend on external services
- ❌ Write tests without assertions
- ❌ Forget to clean up test outputs
- ❌ Test only the happy path
- ❌ Skip testing error handling

## Related Documentation

For detailed testing patterns and examples:
- [Testing Guide](../references/testing.md) - Complete testing strategies and patterns

For component development:
- [Architecture Guide](../guides/component-builder/architecture.md)
- [Best Practices](../guides/component-builder/best-practices.md)
- [Running and Testing](../guides/component-builder/running-and-testing.md)
Weekly Installs
5
Repository
keboola/ai-kit
GitHub Stars
7
First Seen
11 days ago
Installed on
opencode5
gemini-cli5
github-copilot5
codex5
kimi-cli5
cursor5