Skip to main content

Overview

This example shows how to test a REST API using SemanticTest. We’ll test the JSONPlaceholder API, which provides fake online REST API for testing.

Complete Test

test.json
{
  "name": "JSONPlaceholder API Tests",
  "version": "1.0.0",
  "context": {
    "BASE_URL": "https://jsonplaceholder.typicode.com"
  },
  "tests": [
    {
      "id": "get-user",
      "name": "Get User by ID",
      "pipeline": [
        {
          "id": "request",
          "block": "HttpRequest",
          "input": {
            "url": "${BASE_URL}/users/1",
            "method": "GET"
          },
          "output": "response"
        },
        {
          "id": "parse",
          "block": "JsonParser",
          "input": "${response.body}",
          "output": "user"
        },
        {
          "id": "validate",
          "block": "ValidateContent",
          "input": {
            "from": "user.parsed.email",
            "as": "text"
          },
          "config": {
            "matches": ".*@.*\\..*"
          },
          "output": "validation"
        }
      ],
      "assertions": {
        "response.status": 200,
        "user.parsed.id": 1,
        "user.parsed.name": { "contains": "Leanne" },
        "validation.passed": true
      }
    },
    {
      "id": "create-post",
      "name": "Create New Post",
      "pipeline": [
        {
          "id": "create",
          "block": "HttpRequest",
          "input": {
            "url": "${BASE_URL}/posts",
            "method": "POST",
            "headers": {
              "Content-Type": "application/json"
            },
            "body": {
              "title": "Test Post",
              "body": "This is a test post",
              "userId": 1
            }
          },
          "output": "createResponse"
        },
        {
          "id": "parse",
          "block": "JsonParser",
          "input": "${createResponse.body}",
          "output": "post"
        }
      ],
      "assertions": {
        "createResponse.status": 201,
        "post.parsed.title": "Test Post",
        "post.parsed.userId": 1
      }
    },
    {
      "id": "get-posts",
      "name": "Get All Posts",
      "pipeline": [
        {
          "id": "request",
          "block": "HttpRequest",
          "input": {
            "url": "${BASE_URL}/posts",
            "method": "GET"
          },
          "output": "response"
        },
        {
          "id": "parse",
          "block": "JsonParser",
          "input": "${response.body}",
          "output": "posts"
        }
      ],
      "assertions": {
        "response.status": 200
      }
    }
  ]
}

Running the Test

npx semtest test.json
Expected output:
✅ JSONPlaceholder API Tests
  ✅ get-user: Get User by ID (234ms)
     ✅ response.status = 200
     ✅ user.parsed.id = 1
     ✅ user.parsed.name contains "Leanne"
     ✅ validation.passed = true

  ✅ create-post: Create New Post (156ms)
     ✅ createResponse.status = 201
     ✅ post.parsed.title = "Test Post"
     ✅ post.parsed.userId = 1

  ✅ get-posts: Get All Posts (198ms)
     ✅ response.status = 200

3 tests passed, 0 failed (588ms total)

Breaking It Down

Test 1: Get User

1

Make HTTP Request

Fetch user with ID 1 from the API
{
  "block": "HttpRequest",
  "input": {
    "url": "${BASE_URL}/users/1",
    "method": "GET"
  },
  "output": "response"
}
2

Parse JSON Response

Convert JSON string to object
{
  "block": "JsonParser",
  "input": "${response.body}",
  "output": "user"
}
3

Validate Email Format

Check that email matches expected pattern
{
  "block": "ValidateContent",
  "input": {
    "from": "user.parsed.email",
    "as": "text"
  },
  "config": {
    "matches": ".*@.*\\..*"
  }
}
4

Assert Results

Verify all expectations are met
{
  "assertions": {
    "response.status": 200,
    "user.parsed.id": 1,
    "user.parsed.name": { "contains": "Leanne" },
    "validation.passed": true
  }
}

Test 2: Create Post

This test demonstrates POST requests with JSON body:
{
  "block": "HttpRequest",
  "input": {
    "url": "${BASE_URL}/posts",
    "method": "POST",
    "headers": {
      "Content-Type": "application/json"
    },
    "body": {
      "title": "Test Post",
      "body": "This is a test post",
      "userId": 1
    }
  }
}
JSONPlaceholder is a fake API, so it won’t actually create the post. But it simulates the response correctly!

Test 3: Get All Posts

Shows how to validate array responses:
{
  "assertions": {
    "response.status": 200
  }
}
The status assertion verifies we got a successful response.

Variations

With Error Handling

Add retry logic for flaky endpoints:
{
  "pipeline": [
    {
      "id": "request",
      "block": "HttpRequest",
      "input": {
        "url": "${API_URL}/flaky-endpoint",
        "method": "GET"
      },
      "output": "response"
    },
    {
      "block": "Loop",
      "config": {
        "target": "request",
        "maxIterations": 3,
        "condition": {
          "path": "response.status",
          "operator": "notEquals",
          "value": 200
        }
      }
    }
  ],
  "assertions": {
    "response.status": 200
  }
}
This retries the request up to 3 times, looping while the status is not 200.

With Authentication

Add auth token to requests:
{
  "context": {
    "API_KEY": "${env.API_KEY}"
  },
  "tests": [{
    "pipeline": [{
      "block": "HttpRequest",
      "input": {
        "url": "${BASE_URL}/protected",
        "headers": {
          "Authorization": "Bearer ${API_KEY}"
        }
      }
    }]
  }]
}

With Setup/Teardown

Clean up test data:
{
  "setup": [
    {
      "id": "create-test-user",
      "block": "HttpRequest",
      "input": {
        "url": "${BASE_URL}/users",
        "method": "POST",
        "body": { "name": "Test User" }
      },
      "output": "testUser"
    }
  ],
  "tests": [
    {
      "pipeline": [
        {
          "block": "HttpRequest",
          "input": {
            "url": "${BASE_URL}/users/${testUser.body.id}"
          }
        }
      ]
    }
  ],
  "teardown": [
    {
      "block": "HttpRequest",
      "input": {
        "url": "${BASE_URL}/users/${testUser.body.id}",
        "method": "DELETE"
      }
    }
  ]
}

Next Steps

I