E2Engine — Declarative E2E testing

Demo

See the modelexecute.

A small payment system demonstrates HTTP and gRPC, real and mocked dependencies, response verification, downstream interaction expectations, and the same declarative test model across local CLI and Docker/CI execution.

The system

One application. Three dependencies.

The Payment API is the system under test. It calls all three downstream dependencies through E2Engine, allowing E2Engine to mock or proxy them while observing the interactions.

System under testPayment APIHTTP · :8080
HTTPMocked

Fraud Service

Controlled responses determine whether a payment should continue through the flow.

:8081
gRPCReal

Account Service

E2Engine observes and proxies the call to a real gRPC service using an external protobuf contract.

:8082 → :50051
gRPCMocked

Notification Service

E2Engine provides the gRPC service from a protobuf contract defined directly in the environment.

:8083

Payment flow

The boundaries are part of the behavior.

01Fraud

Check whether the payment is approved.

02Account

Debit the account through the real gRPC service.

03Payment

Save the completed payment.

04Notification

Send the payment notification.

05Response

Return the final HTTP result.

Environment

Describe the topology as data.

The environment defines which services participate, whether each dependency is real or mocked, and where E2Engine exposes or forwards each service boundary.

HTTP · mocked

fraud

Response fixtures

gRPC · real

account

External protobuf

gRPC · mocked

notification

Internal protobuf

payment-demo.env.yml · local CLIEnvironment
kind: Environment
version: 1.0.0
name: payment-demo

spec:
  services:
    - id: fraud
      kind: http
      mode: mocked
      address: 127.0.0.1:8081
      fixtures:
        - when:
            http:
              method: POST
              path: /check
          then:
            http:
              status: 200

    - id: account
      kind: grpc
      mode: real
      address: 127.0.0.1:8082
      grpc_target: 127.0.0.1:50051
      proto:
        external:
          file: ./proto/account.proto
          service: account.v1.AccountService

    - id: notification
      kind: grpc
      mode: mocked
      address: 127.0.0.1:8083
      proto:
        internal:
          package: notification.v1
          service: NotificationService

The Docker/CI variant uses the same logical topology with container addresses: E2Engine binds the service boundaries to0.0.0.0, forwards Account calls toaccount:50051, and the Payment API callse2engine:8081, e2engine:8082, ande2engine:8083.

Tests

Three behaviors. One environment.

Each test describes the external result and the downstream interactions that should — or should not — happen.

TestFraudAccountNotificationHTTP
successful-paymentcomplete payment flow✓ 1 call✓ 1 · OK✓ 1 call✓ 201
fraud-rejectionstop after fraud check✓ 1 call✓ 0 calls✓ 0 calls✓ 422
account-rejectionstop after account debit✓ 1 call✓ 1 · FailedPrecondition✓ 0 calls✓ 422

A verified zero is part of the assertion. For example,fraud-rejection requires that neither Account nor Notification is called.

Expected behavior

Responses are only part of the assertion.

Compare the successful path with an early rejection. The test specification describes both what the API returns and what should happen across service boundaries.

201

Successful payment

successful-payment
expectYAML
expect:
  http:
    status: 201
    body: '{"paymentId":"payment-1", ...}'

  calls:
    - service_id: fraud
      count: 1
      http:
        method: POST
        path: /check

    - service_id: account
      count: 1
      grpc:
        method: Debit

    - service_id: notification
      count: 1
      grpc:
        method: Send
422

Fraud rejection

fraud-rejection
expectYAML
expect:
  http:
    status: 422
    body: '{"error":"payment rejected"}'

  calls:
    - service_id: fraud
      count: 1
      http:
        method: POST
        path: /check

    - service_id: account
      count: 0
      grpc:
        method: Debit

    - service_id: notification
      count: 0
      grpc:
        method: Send

Execution

Run all three behaviors as one TestSuite.

Both demo modes create the Environment, the three Tests, and thesmoke TestSuite. The suite then executes the same declarative model against the running application.

terminal
$ e2engine create env ./e2engine/payment-demo.env.yml
$ e2engine create test ./e2engine/successful-payment.test.yml
$ e2engine create test ./e2engine/fraud-rejection.test.yml
$ e2engine create test ./e2engine/account-rejection.test.yml
$ e2engine create ts ./e2engine/smoke.ts.yml

$ e2engine run ts smoke payment-demo

$ e2engine get testsuiteexecutions
$ e2engine get testexecutions

Result

One suite. Three structured executions.

✓PASSED
Testsuccessful-payment
passed
POST/payments201 Created✓

Expected interactions

✓fraudPOST /check1 / 1
✓accountAccountService.Debit1 / 1
✓notificationNotificationService.Send1 / 1

Simplified presentation of one TestExecution in the TestSuiteExecution.

TestExecution · excerptYAML
status: passed
environment_name: payment-demo
test_name: successful-payment

summary:
  response:
    http:
      status_code: 201

  calls:
    - service_id: fraud
      http:
        request:
          method: POST
          path: /check

    - service_id: account
      grpc:
        request:
          rpc: /account.v1.AccountService/Debit

    - service_id: notification
      grpc:
        request:
          rpc: /notification.v1.NotificationService/Send

Negative paths

What did not happen matters too.

In the fraud-rejection test, the API returns 422 after the first dependency rejects the payment. E2Engine verifies that the remaining services were never called.

✓
Fraud1 call
✓
Account0 calls
✓
Notification0 calls
HTTP422

The model

From topology to evidence.

The same structured model connects what participates in the test, what should happen, the execution itself, and what actually happened.

01Environment

What participates

→
02Test

What should happen

→
03Execution

Run the system

→
04Result

What actually happened

What moves into the model?

Less project-specific test orchestration.

The application remains ordinary application code. E2Engine takes responsibility for describing the controlled test environment and expected distributed behavior.

Instead of
  • application-specific mock servers
  • custom proxy and routing setup
  • interaction assertion code
  • framework-specific test topology
E2Engine resources
  • environment specification
  • service fixtures
  • test expectations
  • structured execution results

Run it

One demo. Two execution modes.

Run the same Environment, Tests, and TestSuite locally with an installed E2Engine CLI or entirely through Docker using the published E2Engine image.

$make demo-cli

Builds and starts the Account service and Payment API locally, then executes the suite using the installed e2enginecommand.

$make demo-ci

Builds Linux service images, creates an isolated Docker network, runs E2Engine from ghcr.io/e2engine/cli:0.1.0, verifies the TestSuiteExecution with check tse, and cleans up afterward.

Try it

See the complete executable example.