
Blocks network requests in unit tests, failing fast via a native JVMTI agent; configurable host allowlists/wildcards, Gradle plugin for automatic setup, and per-test annotations.
Unit tests that make real network requests are:
The solution: Automatically fail any test that attempts a network request. Force yourself to use mocks, fakes, or test doubles instead.
@Test
fun `calculates user stats`() {
val stats = userService.calculateStats(userId = 123)
assertEquals(42, stats.totalPurchases)
}What's wrong? This test looks innocent, but if userService internally makes an HTTP request to fetch user data, you've got a hidden network dependency. The test will:
@Test
@BlockNetworkRequests // β Fails immediately if network is accessed
fun `calculates user stats`() {
val fakeService = FakeUserService(
users = listOf(User(id = 123, purchases = 42))
)
val stats = fakeService.calculateStats(userId = 123)
assertEquals(42, stats.totalPurchases) // Fast, reliable, isolated β
}plugins {
id("io.github.garry-jeromson.junit-airgap") version "0.1.0-beta.1"
}That's it for setup! The plugin automatically configures everything.
JUnit 5:
@Test
@BlockNetworkRequests
fun `test with no network access`() {
// This will throw NetworkRequestAttemptedException
Socket("example.com", 80)
}JUnit 4:
@Test
@BlockNetworkRequests
fun testWithNoNetworkAccess() {
// This will throw NetworkRequestAttemptedException
Socket("example.com", 80)
}./gradlew testAny test annotated with @BlockNetworkRequests will now fail fast if it attempts network I/O:
io.github.garryjeromson.junit.airgap.NetworkRequestAttemptedException:
Network request blocked: example.com:80
at MyTest.testWithNoNetworkAccess(MyTest.kt:15)
That's it! π
Uses a JVMTI agent (JVM Tool Interface) to intercept network calls at the native level:
NetworkRequestAttemptedException immediatelyKey benefits:
| Category | Status | Details |
|---|---|---|
| Java | Java 21+ | Single JVMTI agent works across all 21+ versions |
| JUnit | 4.13.2 & 5.11.3 | Both frameworks fully supported |
| Platform | JVM (macOS ARM64) | Native agent for macOS ARM64 |
| Platform | JVM (Linux x86-64) | Native agent for Linux x86-64 |
| Platform | Android (Robolectric) | Full support via Robolectric unit tests |
| HTTP Clients | All major clients | OkHttp, Retrofit, Ktor, Apache, Spring, etc. |
IOS_SUPPORT_INVESTIGATION.md for technical details.For complete compatibility information including:
See the Compatibility Matrix β
Step-by-step instructions for your project type:
Client-specific examples and exception handling:
junitAirgap {
applyToAllTests = true // Block by default
}@Test
fun test1() {
// Network blocked automatically
}
@Test
@AllowNetworkRequests // Opt-out when needed
fun test2() {
// Network allowed
}Perfect for testing with local servers or staging environments:
@Test
@BlockNetworkRequests
@AllowRequestsToHosts(["localhost", "127.0.0.1", "*.staging.mycompany.com"])
fun testWithStagingAPI() {
// β
localhost - allowed
// β
api.staging.mycompany.com - allowed
// β api.production.mycompany.com - blocked
// β external-api.com - blocked
}junitAirgap {
enabled = true
applyToAllTests = false
allowedHosts = listOf("localhost", "*.test.local")
blockedHosts = listOf("*.tracking.com")
debug = false
}More examples: Advanced Configuration Guide β
All tested with comprehensive integration tests:
Core:
Socket, ServerSocket)HttpURLConnection, HttpClient)Popular Libraries:
Exception handling varies by client - some throw NetworkRequestAttemptedException directly, others wrap it in IOException. See HTTP Client Guides for details.
Zero configuration - plugin handles everything automatically:
plugins {
id("io.github.garry-jeromson.junit-airgap") version "0.1.0-beta.1"
}Requires manual configuration (see Setup Guides for details):
dependencies {
testImplementation("io.github.garryjeromson:junit-airgap:0.1.0-beta.1")
}The JVMTI agent loads once at JVM startup and has minimal overhead:
From benchmark suite (100 iterations, Java 21):
| Test Type | Overhead | Notes |
|---|---|---|
| Empty Test | +458 ns (+183%) | High % but negligible absolute time |
| Array Sorting (4.2ms) | +270 ΞΌs (+6.4%) | Realistic test - low overhead |
Key insight: Small constant overhead appears as high percentage for nanosecond operations, but is negligible for real tests.
Run benchmarks: make benchmark
Learn more about JVMTI performance β
# Run all tests
make test
# Run specific test suites
make test-jvm # JVM only
make test-android # Android only
make test-integration # Integration tests
make test-plugin-integration # Plugin integration tests
# Test on Linux locally (requires Docker)
make docker-build-linux # Build Linux Docker image (one-time)
make docker-test-linux # Run tests in Linux containerDocker Multi-Platform Testing: Test Linux builds locally before pushing to CI for faster feedback loops. See Docker Local Testing Guide β
Checklist:
@BlockNetworkRequests annotation present?@ExtendWith(AirgapExtension::class) on class?-Djunit.airgap.debug=true)Issue Fixed in v0.1.0-beta.2+
If you're using an older version and encounter "platform encoding not initialized" errors when running tests via IntelliJ:
Workaround 1: Configure IntelliJ to use Gradle
Preferences β Build, Execution, Deployment β Build Tools β Gradle
Set "Run tests using" to "Gradle" instead of "IntelliJ IDEA"
Workaround 2: Run tests via Gradle
./gradlew test --tests "YourTestClass"Solution: Upgrade to v0.1.0-beta.2 or later for full IDE support.
WARNING: JVMTI agent not found at: /path/to/agent.dylib
Solution:
./gradlew clean build # Rebuild to extract agentVerify plugin is enabled:
junitAirgap {
enabled = true
}java.lang.UnsatisfiedLinkError: no junit-airgap-agent in java.library.path
Current support: macOS ARM64, Linux x86-64
Coming soon: Linux ARM64
See Platform Compatibility for details.
See detailed information about what's being blocked:
./gradlew test -Djunit.airgap.debug=trueOr in build.gradle.kts:
tasks.test {
systemProperty("junit.airgap.debug", "true")
}Complete working examples in plugin-integration-tests/:
Contributions welcome! See CONTRIBUTING.md for guidelines.
MIT License - See LICENSE for details
Built with comprehensive test coverage across all platforms, frameworks, and HTTP clients.
This project was inspired by similar network blocking tools in other ecosystems:
Made with β€οΈ for better unit tests
Unit tests that make real network requests are:
The solution: Automatically fail any test that attempts a network request. Force yourself to use mocks, fakes, or test doubles instead.
@Test
fun `calculates user stats`() {
val stats = userService.calculateStats(userId = 123)
assertEquals(42, stats.totalPurchases)
}What's wrong? This test looks innocent, but if userService internally makes an HTTP request to fetch user data, you've got a hidden network dependency. The test will:
@Test
@BlockNetworkRequests // β Fails immediately if network is accessed
fun `calculates user stats`() {
val fakeService = FakeUserService(
users = listOf(User(id = 123, purchases = 42))
)
val stats = fakeService.calculateStats(userId = 123)
assertEquals(42, stats.totalPurchases) // Fast, reliable, isolated β
}plugins {
id("io.github.garry-jeromson.junit-airgap") version "0.1.0-beta.1"
}That's it for setup! The plugin automatically configures everything.
JUnit 5:
@Test
@BlockNetworkRequests
fun `test with no network access`() {
// This will throw NetworkRequestAttemptedException
Socket("example.com", 80)
}JUnit 4:
@Test
@BlockNetworkRequests
fun testWithNoNetworkAccess() {
// This will throw NetworkRequestAttemptedException
Socket("example.com", 80)
}./gradlew testAny test annotated with @BlockNetworkRequests will now fail fast if it attempts network I/O:
io.github.garryjeromson.junit.airgap.NetworkRequestAttemptedException:
Network request blocked: example.com:80
at MyTest.testWithNoNetworkAccess(MyTest.kt:15)
That's it! π
Uses a JVMTI agent (JVM Tool Interface) to intercept network calls at the native level:
NetworkRequestAttemptedException immediatelyKey benefits:
| Category | Status | Details |
|---|---|---|
| Java | Java 21+ | Single JVMTI agent works across all 21+ versions |
| JUnit | 4.13.2 & 5.11.3 | Both frameworks fully supported |
| Platform | JVM (macOS ARM64) | Native agent for macOS ARM64 |
| Platform | JVM (Linux x86-64) | Native agent for Linux x86-64 |
| Platform | Android (Robolectric) | Full support via Robolectric unit tests |
| HTTP Clients | All major clients | OkHttp, Retrofit, Ktor, Apache, Spring, etc. |
IOS_SUPPORT_INVESTIGATION.md for technical details.For complete compatibility information including:
See the Compatibility Matrix β
Step-by-step instructions for your project type:
Client-specific examples and exception handling:
junitAirgap {
applyToAllTests = true // Block by default
}@Test
fun test1() {
// Network blocked automatically
}
@Test
@AllowNetworkRequests // Opt-out when needed
fun test2() {
// Network allowed
}Perfect for testing with local servers or staging environments:
@Test
@BlockNetworkRequests
@AllowRequestsToHosts(["localhost", "127.0.0.1", "*.staging.mycompany.com"])
fun testWithStagingAPI() {
// β
localhost - allowed
// β
api.staging.mycompany.com - allowed
// β api.production.mycompany.com - blocked
// β external-api.com - blocked
}junitAirgap {
enabled = true
applyToAllTests = false
allowedHosts = listOf("localhost", "*.test.local")
blockedHosts = listOf("*.tracking.com")
debug = false
}More examples: Advanced Configuration Guide β
All tested with comprehensive integration tests:
Core:
Socket, ServerSocket)HttpURLConnection, HttpClient)Popular Libraries:
Exception handling varies by client - some throw NetworkRequestAttemptedException directly, others wrap it in IOException. See HTTP Client Guides for details.
Zero configuration - plugin handles everything automatically:
plugins {
id("io.github.garry-jeromson.junit-airgap") version "0.1.0-beta.1"
}Requires manual configuration (see Setup Guides for details):
dependencies {
testImplementation("io.github.garryjeromson:junit-airgap:0.1.0-beta.1")
}The JVMTI agent loads once at JVM startup and has minimal overhead:
From benchmark suite (100 iterations, Java 21):
| Test Type | Overhead | Notes |
|---|---|---|
| Empty Test | +458 ns (+183%) | High % but negligible absolute time |
| Array Sorting (4.2ms) | +270 ΞΌs (+6.4%) | Realistic test - low overhead |
Key insight: Small constant overhead appears as high percentage for nanosecond operations, but is negligible for real tests.
Run benchmarks: make benchmark
Learn more about JVMTI performance β
# Run all tests
make test
# Run specific test suites
make test-jvm # JVM only
make test-android # Android only
make test-integration # Integration tests
make test-plugin-integration # Plugin integration tests
# Test on Linux locally (requires Docker)
make docker-build-linux # Build Linux Docker image (one-time)
make docker-test-linux # Run tests in Linux containerDocker Multi-Platform Testing: Test Linux builds locally before pushing to CI for faster feedback loops. See Docker Local Testing Guide β
Checklist:
@BlockNetworkRequests annotation present?@ExtendWith(AirgapExtension::class) on class?-Djunit.airgap.debug=true)Issue Fixed in v0.1.0-beta.2+
If you're using an older version and encounter "platform encoding not initialized" errors when running tests via IntelliJ:
Workaround 1: Configure IntelliJ to use Gradle
Preferences β Build, Execution, Deployment β Build Tools β Gradle
Set "Run tests using" to "Gradle" instead of "IntelliJ IDEA"
Workaround 2: Run tests via Gradle
./gradlew test --tests "YourTestClass"Solution: Upgrade to v0.1.0-beta.2 or later for full IDE support.
WARNING: JVMTI agent not found at: /path/to/agent.dylib
Solution:
./gradlew clean build # Rebuild to extract agentVerify plugin is enabled:
junitAirgap {
enabled = true
}java.lang.UnsatisfiedLinkError: no junit-airgap-agent in java.library.path
Current support: macOS ARM64, Linux x86-64
Coming soon: Linux ARM64
See Platform Compatibility for details.
See detailed information about what's being blocked:
./gradlew test -Djunit.airgap.debug=trueOr in build.gradle.kts:
tasks.test {
systemProperty("junit.airgap.debug", "true")
}Complete working examples in plugin-integration-tests/:
Contributions welcome! See CONTRIBUTING.md for guidelines.
MIT License - See LICENSE for details
Built with comprehensive test coverage across all platforms, frameworks, and HTTP clients.
This project was inspired by similar network blocking tools in other ecosystems:
Made with β€οΈ for better unit tests