JAllele CLI Examples
This document demonstrates the command-line interface for JAllele with real-world examples.
Basic Usage
Specify classes explicitly:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 10 \
--junit \
--source-classes com.example.MyClass \
--test-classes com.example.MyClassTest
Example 1: Testing a Single Module Project
For a typical Maven or Gradle project with compiled classes:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 100 \
--junit \
--source-path target/classes \
--source-patterns 'com.example.**' \
--test-path target/test-classes \
--test-patterns 'com.example.**Test'
Explanation:
--source-path target/classes
: Points to compiled source classes--source-patterns 'com.example.**'
: Matches all classes under com.example package and subpackages--test-path target/test-classes
: Points to compiled test classes--test-patterns 'com.example.**Test'
: Matches all test classes ending with ‘Test’
Example 2: Testing with JAR Files
When your project is packaged as JAR files:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 50 \
--junit \
--source-path myproject-1.0.jar \
--source-patterns 'com.example.**' \
--test-path myproject-tests-1.0.jar \
--test-patterns 'com.example.**Test'
Example 3: Multi-Module Project
For projects with multiple modules (e.g., Maven multi-module projects):
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 100 \
--junit \
--source-path module1/target/classes module2/target/classes module3/target/classes \
--source-patterns 'com.example.**' \
--test-path module1/target/test-classes module2/target/test-classes module3/target/test-classes \
--test-patterns 'com.example.**Test'
Example 4: Mixed JAR and Directory Paths
You can mix JAR files and directories:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 50 \
--junit \
--source-path core/target/classes api.jar utils/target/classes \
--source-patterns 'com.example.**' \
--test-path core/target/test-classes api-tests.jar utils/target/test-classes \
--test-patterns '**.*Test'
Pattern Syntax
Wildcards
*
- Matches any characters within a single package segment (does not cross package boundaries)**
- Matches any number of package segments (including zero)
Examples
Pattern | Matches | Does Not Match |
---|---|---|
com.example.* | com.example.MyClass | com.example.sub.MyClass |
com.example.** | com.example.MyClass com.example.sub.MyClass com.example.sub.deep.MyClass | com.other.MyClass |
**Test | com.example.MyTest MyTest org.test.SomeTest | com.example.MyTestHelper |
*Test | MyTest SimpleTest | com.example.sub.MyTest |
com.example.*.Test | com.example.MyTest com.example.SimpleTest | com.example.sub.MyTest |
Real-World Example: Simple Calculator Project
Here’s a complete working example you can try:
Step 1: Create the Project
# Create project structure
mkdir -p calculator-project/src/main/java/com/example
mkdir -p calculator-project/src/test/java/com/example
cd calculator-project
# Create Calculator.java
cat > src/main/java/com/example/Calculator.java << 'EOF'
package com.example;
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public int divide(int a, int b) {
if (b == 0) throw new IllegalArgumentException("Division by zero");
return a / b;
}
}
EOF
# Create CalculatorTest.java
cat > src/test/java/com/example/CalculatorTest.java << 'EOF'
package com.example;
import org.junit.Test;
import static org.junit.Assert.*;
public class CalculatorTest {
@Test
public void testAdd() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3));
}
@Test
public void testSubtract() {
Calculator calc = new Calculator();
assertEquals(1, calc.subtract(3, 2));
}
@Test
public void testMultiply() {
Calculator calc = new Calculator();
assertEquals(6, calc.multiply(2, 3));
}
@Test
public void testDivide() {
Calculator calc = new Calculator();
assertEquals(2, calc.divide(6, 3));
}
@Test(expected = IllegalArgumentException.class)
public void testDivideByZero() {
Calculator calc = new Calculator();
calc.divide(5, 0);
}
}
EOF
# Create pom.xml
cat > pom.xml << 'EOF'
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>calculator</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
EOF
# Compile the project
mvn clean test-compile
Step 2: Run JAllele Mutation Testing
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 10 \
--junit \
--source-path target/classes \
--source-patterns 'com.example.**' \
--test-path target/test-classes \
--test-patterns 'com.example.**Test' \
--log-level INFO
Expected Output:
- JAllele will discover the Calculator class and CalculatorTest
- It will introduce 10 mutations to the Calculator class
- The tests should detect most or all of the mutations
- You’ll see a summary like
Results: 10/10 (1.0)
indicating all mutations were caught
Real-World Example: Your Own Project
JAllele works best with projects that follow standard Java testing conventions (public test classes). Here’s how to use it with your Maven or Gradle project:
Maven Projects
# Build your project
mvn clean test-compile
# Run JAllele
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 100 \
--junit \
--source-path target/classes \
--source-patterns 'com.yourcompany.yourproject.**' \
--test-path target/test-classes \
--test-patterns 'com.yourcompany.yourproject.**Test' \
--log-level INFO
Gradle Projects
# Build your project
./gradlew compileJava compileTestJava
# Run JAllele
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 100 \
--junit \
--source-path build/classes/java/main \
--source-patterns 'com.yourcompany.yourproject.**' \
--test-path build/classes/java/test \
--test-patterns 'com.yourcompany.yourproject.**Test' \
--log-level INFO
Multi-Module Projects
For multi-module Maven or Gradle projects:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 100 \
--junit \
--source-path module1/target/classes module2/target/classes \
--source-patterns 'com.yourcompany.**' \
--test-path module1/target/test-classes module2/target/test-classes \
--test-patterns 'com.yourcompany.**Test' \
--log-level INFO
Note on Test Class Visibility: JUnit requires test classes to be public. Some open-source projects (like Apache Commons Lang) use package-private test classes which cannot be discovered and run through JAllele’s pattern-based approach. If you encounter this, use explicit class names with --source-classes
and --test-classes
options instead.
TestNG Support
All examples work with TestNG as well - just replace --junit
with --testng
:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 100 \
--testng \
--source-path target/classes \
--source-patterns 'com.example.**' \
--test-path target/test-classes \
--test-patterns 'com.example.**Test'
Combining Explicit Classes and Patterns
You can also explicitly specify classes while using classpath for test discovery:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 50 \
--junit \
--source-classes com.example.MyClass com.example.OtherClass \
--test-path target/test-classes \
--test-patterns '**Test'
Or vice versa:
java -Djdk.attach.allowAttachSelf=true -jar jallele.jar \
--count 50 \
--junit \
--source-path target/classes \
--source-patterns 'com.example.**' \
--test-classes com.example.MyTest com.example.OtherTest
Tips and Best Practices
- Start with a small count: Use
--count 10
initially to verify everything works - Use specific patterns: More specific patterns run faster (e.g.,
com.example.utils.**
instead ofcom.example.**
) - Enable logging: Use
--log-level FINE
for debugging class discovery issues - Check discovered classes: Set
--log-level FINE
to see which classes are discovered
Troubleshooting
No classes discovered
If you get an error about no classes being found:
- Verify the paths exist:
ls -la target/classes
- Check the pattern matches: Use
--log-level FINE
to see discovery output - Ensure classes are compiled: Run
mvn test-compile
orgradle testClasses
Pattern not matching
- Remember
*
does NOT cross package boundaries - Use
**
to match across packages - Patterns are matched against fully qualified class names (e.g.,
com.example.MyClass
)
Performance considerations
- More specific patterns = faster class discovery
- Limit the scope to the classes you actually want to test
- Use multiple targeted runs instead of one large run