A Domain-Specific Language (DSL) for describing HTTP API tests that compiles to runnable JUnit 5 tests using Java's built-in HttpClient.
TestLang++ allows you to write concise, readable HTTP API tests in a custom DSL syntax. The compiler parses .test files and generates complete JUnit 5 test classes with assertions, ready to run against your backend.
- ✅ Simple DSL syntax for GET, POST, PUT, DELETE requests
- ✅ Variable substitution for dynamic values
- ✅ Configurable base URL and default headers
- ✅ Rich assertions for status codes, headers, and body content
- ✅ Generates idiomatic JUnit 5 code using
java.net.http.HttpClient - ✅ Meaningful error messages for invalid syntax
- ✅ Bonus features: Status range assertions, triple-quoted strings
TestLang-Java/
├── src/
│ ├── main/
│ │ ├── java/com/testlang/
│ │ │ ├── Main.java # Compiler entry point
│ │ │ ├── ast/ # AST node classes
│ │ │ │ ├── Program.java
│ │ │ │ ├── ConfigBlock.java
│ │ │ │ ├── Variable.java
│ │ │ │ ├── TestBlock.java
│ │ │ │ ├── Request.java
│ │ │ │ ├── Assertion.java
│ │ │ │ └── ...
│ │ │ └── codegen/
│ │ │ └── CodeGenerator.java # JUnit 5 code generator
│ │ ├── jflex/
│ │ │ └── lexer.flex # JFlex lexer specification
│ │ └── cup/
│ │ └── parser.cup # CUP parser specification
├── backend/ # Reference Spring Boot backend
│ ├── src/main/java/com/testlang/backend/
│ │ ├── TestLangBackendApplication.java
│ │ └── ApiController.java
│ └── pom.xml
├── example.test # Example test file
├── invalid.test # Invalid test for error demo
├── pom.xml # Maven configuration
├── build.bat # Build compiler
├── compile-test.bat # Compile .test to .java
├── run-tests.bat # Run generated tests
├── run-backend.bat # Start backend server
└── README.md # This file
- Java 11 or higher
- Maven 3.6+
- Windows (scripts provided for Windows; adapt for Linux/Mac)
build.batThis will:
- Generate the lexer from
lexer.flexusing JFlex - Generate the parser from
parser.cupusing CUP - Compile all Java sources
- Create an executable JAR with dependencies
Open a new terminal and run:
run-backend.batWait until you see:
Started TestLangBackendApplication in X.XXX seconds
The server will be available at http://localhost:8080
In your original terminal:
compile-test.bat example.test GeneratedTests.javaThis reads example.test and generates GeneratedTests.java
run-tests.batYou should see JUnit test results with all tests passing! ✅
config {
base_url = "http://localhost:8080";
header "Content-Type" = "application/json";
}
let variableName = "value";
let numericVar = 42;
test TestName {
// HTTP requests and assertions
}
config {
base_url = "http://localhost:8080";
header "Content-Type" = "application/json";
header "X-Custom-Header" = "value";
}
base_url: Prepended to paths starting with/header: Default headers applied to all requests
let username = "admin";
let userId = 42;
Use variables in paths and strings with $name:
GET "/api/users/$userId";
POST "/api/login" {
body = "{ \"username\": \"$username\" }";
}
GET "/api/users/42";
DELETE "/api/users/42";
POST "/api/login" {
header "Content-Type" = "application/json";
body = "{ \"username\": \"admin\", \"password\": \"1234\" }";
}
PUT "/api/users/42" {
body = "{ \"role\": \"ADMIN\" }";
}
expect status = 200;
expect header "Content-Type" = "application/json";
expect header "Content-Type" contains "json";
expect body contains "\"token\":";
expect status in 200..299; // Bonus: range check
Requirements:
- Each test must have ≥1 request
- Each test must have ≥2 assertions
test Login {
POST "/api/login" {
body = "{ \"username\": \"admin\", \"password\": \"1234\" }";
}
expect status = 200;
expect header "Content-Type" contains "json";
expect body contains "\"token\":";
}
The included Spring Boot backend provides these endpoints:
| Endpoint | Method | Description | Example Request |
|---|---|---|---|
/api/login |
POST | Authenticate user | {"username":"admin","password":"1234"} |
/api/users/{id} |
GET | Get user by ID | - |
/api/users/{id} |
PUT | Update user | {"role":"ADMIN"} |
/api/users/{id} |
DELETE | Delete user | - |
# Login
curl -X POST http://localhost:8080/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"1234"}'
# Get user
curl http://localhost:8080/api/users/42
# Update user
curl -X PUT http://localhost:8080/api/users/42 \
-H "Content-Type: application/json" \
-d '{"role":"ADMIN"}'
# Delete user
curl -X DELETE http://localhost:8080/api/users/999java -jar target\testlang-java-1.0.0-jar-with-dependencies.jar mytest.test MyTests.javacompile-test.bat invalid.testThis demonstrates:
- Lexical errors (invalid identifiers)
- Syntax errors (missing semicolons)
- Validation errors (too few assertions)
- Tokenizes input into: keywords, identifiers, strings, numbers, operators
- Handles comments and whitespace
- Supports escape sequences in strings
- Builds an Abstract Syntax Tree (AST)
- Enforces grammar rules
- Provides error recovery and meaningful messages
- Traverses the AST
- Generates JUnit 5 test methods
- Handles variable substitution
- Produces idiomatic Java code using
HttpClient
import org.junit.jupiter.api.*;
import java.net.http.*;
public class GeneratedTests {
static HttpClient client;
static String BASE = "http://localhost:8080";
static Map<String,String> DEFAULT_HEADERS = new HashMap<>();
@BeforeAll
static void setup() {
client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
}
@Test
void test_Login() throws Exception {
// Build and send request
// Assert response
}
}- ✅ Status range assertions:
expect status in 200..299; - ✅ Triple-quoted strings:
"""multiline string""" - ✅ Comprehensive error messages with line/column info
- ✅ Request-specific headers override defaults
- ✅ Validation ensures tests have requests and assertions
TestLang++ Compiler
===================
Input: example.test
Output: GeneratedTests.java
Parsing...
✓ Parse successful
✓ Validation successful
Generating JUnit 5 code...
✓ Code generation successful
Generated 6 test(s)
Output written to: GeneratedTests.java
========================================
Running Tests
========================================
Test run finished after 1234 ms
[ 6 containers found ]
[ 0 containers skipped ]
[ 6 containers started ]
[ 0 containers aborted ]
[ 6 containers successful ]
[ 0 containers failed ]
[ 6 tests found ]
[ 0 tests skipped ]
[ 6 tests started ]
[ 0 tests aborted ]
[ 6 tests successful ]
[ 0 tests failed ]
Error: Line 10, Column 5: Illegal character <@>
Syntax error at line 12, column 3
Validation error: Test 'Login' must contain at least 2 assertions (found 1)
This is an academic project for SE2062. Contributions are welcome for educational purposes.
MIT License - See LICENSE file
Student ID: IT23657250
Course: SE2062 - Compilers
Assignment: TestLang++ DSL Implementation
-
Introduction (15s)
- Show project structure
- Explain what TestLang++ does
-
Valid Test Compilation (60s)
- Show
example.testfile - Run
compile-test.bat example.test - Show generated
GeneratedTests.java
- Show
-
Running Tests (45s)
- Start backend with
run-backend.bat - Run
run-tests.bat - Show all tests passing
- Start backend with
-
Error Handling (30s)
- Show
invalid.test - Run
compile-test.bat invalid.test - Show error messages
- Show
-
Conclusion (10s)
- Recap features
- Thank you
Total Time: ~2:40 minutes
Happy Testing! 🚀 TestLang++ (Java) – Backend API Testing DSL