Q26 of 40 · Karate

How would you architect a Karate framework for a microservices team?

KarateSeniorkaratemicroservicesarchitectureci-cdapi-testing

Short answer

Short answer: Organise feature files by service domain under a shared test repository. Use karate-config.js to route baseUrl per service via environment variables. Share authentication and common data-setup features in a helpers/ module. Run each service's feature set in parallel — Karate's runner supports multi-path configuration and tag-based filtering.

Detail

Repository strategy: centralised vs per-service.

Centralised test repo (recommended for small-to-medium teams): one repo with subfolders per service. Easier to share helpers, run cross-service flows, and enforce standards.

Per-service test submodule: each service owns its own Karate tests in a Maven submodule. Better separation of concerns but harder to write cross-service flows.

Typical central structure:

features/
  user-service/
  order-service/
  payment-service/
  notification-service/
helpers/
  auth/
    get-token.feature
  data/
    create-user.feature
    create-order.feature
karate-config.js    ← routes per service

karate-config.js for multiple service URLs:

config.userServiceUrl    = env === 'staging' ? 'https://users.staging.svc' : 'http://localhost:8081';
config.orderServiceUrl   = env === 'staging' ? 'https://orders.staging.svc' : 'http://localhost:8082';
config.paymentServiceUrl = env === 'staging' ? 'https://payments.staging.svc' : 'http://localhost:8083';

Tag-based CI: each service's CI pipeline runs only its tagged tests:

mvn test -Dkarate.env=staging -Dkarate.options="--tags @user-service"

Cross-service flow tests: tag them @integration and run in a nightly suite that requires all services to be up.

// EXAMPLE

SuiteRunner.java

// Run tests for all services in parallel
class FullSuiteRunner {
    @Test
    void runAllMicroserviceTests() {
        Results results = Runner
            .path(
                "classpath:features/user-service",
                "classpath:features/order-service",
                "classpath:features/payment-service"
            )
            .tags("~@wip", "~@manual")
            .outputJunitXml(true)
            .parallel(8);   // 8 threads for ~3 services × 2-3 parallel features each
        assertThat(results.getFailCount()).isZero();
    }
}

// Run a single service's tests (per-service CI)
class UserServiceRunner {
    @Test
    void runUserServiceTests() {
        Results results = Runner
            .path("classpath:features/user-service")
            .tags("@user-service", "~@wip")
            .parallel(4);
        assertThat(results.getFailCount()).isZero();
    }
}

// WHAT INTERVIEWERS LOOK FOR

Centralised vs per-service repo trade-off, multi-service URL routing in karate-config.js, tag-based CI execution for per-service pipelines, and separating cross-service integration tests into a nightly suite. This is an architect-level answer.

// COMMON PITFALL

One monolithic SuiteRunner with no tag filtering — every PR for any service runs the entire test suite. Tag-based filtering and per-service runners are essential for CI speed as the team grows.