Skip to content

Your first test

In this guide, you will create and run a minimal E2Engine test against a real HTTP service.

The service exposes one endpoint:

GET /ok → 200 OK

You will describe the service as an E2Engine Environment, define its expected behavior as a Test, execute it, and inspect the resulting TestExecution.

Make sure E2Engine is installed and available:

Terminal window
e2engine version

For this example, we also need a small HTTP service.

Create main.go:

package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/ok", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
})
if err := http.ListenAndServe(":9000", nil); err != nil {
log.Fatal(err)
}
}

Start the service:

Terminal window
go run main.go

It listens on port 9000 and returns HTTP 200 OK for GET /ok.

You can verify it directly:

Terminal window
curl -i http://127.0.0.1:9000/ok

An E2Engine environment describes the services that participate in a test.

Create environment.yml:

kind: Environment
version: 1.0.0
name: direct-real-http
description: one real HTTP service that returns 200 OK for /ok
spec:
services:
- id: direct-real-http
kind: http
mode: real
address: 127.0.0.1:8083
http_target: http://127.0.0.1:9000

This environment contains one service:

E2Engine
│
│ 127.0.0.1:8083
▼
direct-real-http
│
│ forwards to
▼
http://127.0.0.1:9000
│
▼
/ok

The important distinction is between address and http_target.

address: 127.0.0.1:8083
http_target: http://127.0.0.1:9000

address is the endpoint exposed through E2Engine during the test.

http_target is the actual address of the real service.

Requests sent to 127.0.0.1:8083 are therefore observed by E2Engine and forwarded to the application listening on port 9000.

Create the environment:

Terminal window
e2engine create environment environment.yml

You can inspect it with:

Terminal window
e2engine get environment direct-real-http

Create test.yml:

kind: Test
version: 1.0.0
name: direct-real-http
description: verifies a request to a real HTTP service
spec:
request:
http:
method: GET
url: http://127.0.0.1:8083/ok
expect:
http:
status: 200
calls:
- service_id: direct-real-http
http:
method: GET
path: /ok

The test describes both the request to execute and the behavior to verify.

The request:

request:
http:
method: GET
url: http://127.0.0.1:8083/ok

is sent through the service address defined by the environment.

The first expectation:

expect:
http:
status: 200

requires the test request to return HTTP 200.

The second expectation:

calls:
- service_id: direct-real-http
http:
method: GET
path: /ok

requires E2Engine to observe a matching GET /ok interaction with the direct-real-http service.

Because count is not specified, the expectation means that a matching call must occur at least once.

Create the test:

Terminal window
e2engine create test test.yml

You can inspect it with:

Terminal window
e2engine get test direct-real-http

Run the test in the environment:

Terminal window
e2engine run test direct-real-http direct-real-http

E2Engine creates a TestExecution:

created test execution with id: f5497dfbbf8392b4828f5323c07e4b5cc8026790caca7b3065350413bebbd4e3

During the execution, the request follows this path:

Test
│
│ GET http://127.0.0.1:8083/ok
▼
E2Engine
│
│ observes GET /ok
│
│ forwards request
▼
Real HTTP service
127.0.0.1:9000
│
│ 200 OK
▼
E2Engine
│
▼
Test result

E2Engine can therefore verify both the externally visible response and the interaction with the real service.

Use the execution ID returned by the previous command:

Terminal window
e2engine get testexecution f5497dfbbf8392b4828f5323c07e4b5cc8026790caca7b3065350413bebbd4e3

You can also use the short resource name and a unique ID prefix:

Terminal window
e2engine get te f5497

The result looks like this:

id: f5497dfbbf8392b4828f5323c07e4b5cc8026790caca7b3065350413bebbd4e3
started_at: 2026-08-19T15:16:47.974744Z
finished_at: 2026-08-19T15:16:47.993394Z
status: passed
environment_name: direct-real-http
test_name: direct-real-http
summary:
request:
http:
method: GET
url: http://127.0.0.1:8083/ok
response:
http:
status_code: 200
expect:
http:
status_code: 200
calls:
- service_id: direct-real-http
http:
method: GET
path: /ok
calls:
- service_id: direct-real-http
http:
request:
method: GET
path: /ok
headers:
Accept-Encoding:
- gzip
User-Agent:
- Go-http-client/1.1
response:
status_code: 200
headers:
Content-Length:
- "0"

The execution contains several distinct pieces of information.

request:
http:
method: GET
url: http://127.0.0.1:8083/ok

This is the request E2Engine executed.

response:
http:
status_code: 200

This is the response that was actually observed.

expect:
http:
status_code: 200
calls:
- service_id: direct-real-http
http:
method: GET
path: /ok

These are the conditions defined by the test.

calls:
- service_id: direct-real-http
http:
request:
method: GET
path: /ok
response:
status_code: 200

These are the service interactions E2Engine actually observed during execution.

Finally:

status: passed

means the observed behavior satisfied the test expectations.

This small example already exercises the core E2Engine model:

Environment
│
▼
Test
│
▼
Execution
│
▼
Result

The Environment described a real HTTP service and how E2Engine could reach it.

The Test described the request, expected response, and expected service interaction.

The Execution ran that test inside the environment.

The Result recorded the request, response, expectations, and observed calls in a structured form.

The important part is that the test did not only assert:

GET /ok → 200

It also verified that the expected interaction occurred across the service boundary.

This example deliberately uses a single real HTTP service.

E2Engine environments can also combine:

  • real and mocked services;
  • HTTP and gRPC;
  • response fixtures;
  • external and internally defined protobuf services;
  • interaction counts and request matching;
  • multiple tests executed against the same environment.

The demo builds on exactly the same model with a Payment API that communicates with a mocked HTTP Fraud service, a real gRPC Account service, and a mocked gRPC Notification service.

The model stays the same. Only the environment becomes more interesting.