Java SDK tutorial, times10 method
You are a 10x programmer and will soon have your annual performance review, so you are writing a method that takes any number and multiplies it by ten. That way if anybody names some productivity, you can respond with your ten times greater productivity.
You write the method in Java and at first you use print debugging. However, this performance review is very important for your career so you decide not to take any chances. You ultimately decide to thoroughly test this method using the Antithesis Java SDK.
The rest of the document walks you through this process.
Recall that the basic SDK workflow is:
- Import the SDK classes that are being used.
- Use SDK classes in your code.
- Build your code, including the SDK as a build-time dependency.
- Deploy your build.
Creating your project
Step 0: Create your project
You begin by writing your class in Java with printf
debugging. Make a directory where you would like to create your Java project and cd
to that directory.
mkdir -p myapp/company
mkdir myapp/lib
cd myapp
Create a file myapp/company/myapp.java
, with the following content:
package company;
public class myapp {
static int times10(int x) {
int result = x * 10;
return result;
}
public static void main(String[] args) {
System.out.printf("Hello, world!\n");
System.out.printf("%d x 10 = %d\n", 3, times10(3));
System.out.printf("%d x 10 = %d\n", 8, times10(8));
}
}
Now build and run. For convenience, this is displayed as two separate steps: compiling and linking.
cd myapp
javac company/myapp.java
java -cp . company.myapp
and it prints
Hello, world!
3 x 10 = 30
8 x 10 = 80
Using our SDK classes
Step 1: Import the Java SDK assert class and jackson dependencies
For this example, the following jars are obtained from Maven Central, and
stored in the myapp/lib
directory:
com.fasterxml.jackson.core:jackson-databind:2.2.3
com.fasterxml.jackson.core:jackson-core:2.2.3
com.fasterxml.jackson.core:jackson-annotations:2.2.3
com.antithesis.ffi:antithesis-ffi:1.3.1
com.antithesis.sdk:antithesis-sdk:1.3.1
Import the needed dependencies:
import static com.antithesis.sdk.Assert.*;
import static com.antithesis.sdk.Random.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
Step 2. Use SDK classes in your code
You wrote a method to multiply numbers by ten. What sorts of properties should it have? The output should always be even. You should also make sure you are testing a wide variety of inputs, such as both even and odd numbers.
Modify the code. The modifications are individually explained below.
package company;
import static com.antithesis.sdk.Assert.*;
import static com.antithesis.sdk.Random.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class myapp {
static int times10(int x) {
ObjectMapper mapper = new ObjectMapper();
// Input value
ObjectNode input_details = mapper.createObjectNode();
input_details.put("input", x);
sometimes(x % 2 == 1, "input is sometimes odd", input_details);
// Calculated result
int result = x * 10;
ObjectNode result_details = mapper.createObjectNode();
result_details.put("result", result);
always(result % 2 == 0, "result is always even", result_details);
return result;
}
public static void main(String[] args) {
System.out.printf("Hello, world!\n");
for (int i = 0; i < 50; i++) {
int x = (int)(getRandom() % 500L);
System.out.printf("%d x 10 = %d\n", x, times10(x));
}
}
}
-
Lines 3-6 You imported the Antithesis Java SDK and Jackson JSON support.
-
You added two assertions to times10.
-
Line 22 You assert that the result is even using an Always Assertion. This is a fairly conventional assertion.
-
Line 16 You insert a Sometimes Assertion, or an assertion that something will happen at least once across the entire testing session. You assert that sometimes during testing, the method will be called with an odd argument.
The two types of assertions complement one another: Always Assertions assert that the behavior is correct, whereas Sometimes Assertions assert that you are testing under wide enough conditions to surface any potential incorrect behavior. In this case, the output would trivially be even if you only passed it even inputs – you must ensure your properties are not being trivially satisfied!
-
Lines 29-32 You use randomness to call the method with many random values (between 0 and 500). Previously, you called the method with hardcoded values but now you will pass the function method values and test that the output is always even. This approach is more powerful but makes Sometimes Assertions necessary – now you must test that you are passing the method odd values, whereas previously the tests were hardcoded so you were certain that you were passing it odd values.
-
You call the method getRandom
to draw a random number, and then pass this random number to times10
. You use a loop to do this fifty times in a row. Every time the times10 method is called in this loop, it triggers the assertions in lines 16 & 22. In summary: You will test times10
by passing it a random integer fifty times in a row. You have asserted that all fifty outputs must be even and that at least one random input must be odd.
You can now build and run your project:
cd myapp
# Build Project
javac -cp \
lib/antithesis-ffi-1.3.0.jar:\
lib/antithesis-sdk-1.3.0.jar:\
lib/jackson-databind-2.2.3.jar:\
lib/jackson-core-2.2.3.jar:\
lib/jackson-annotations-2.2.3.jar \
company/*.java
# Run Project
java -cp .:\
lib/antithesis-ffi-1.3.0.jar:\
lib/antithesis-sdk-1.3.0.jar:\
lib/jackson-databind-2.2.3.jar:\
lib/jackson-core-2.2.3.jar:\
lib/jackson-annotations-2.2.3.jar:\
company.myapp.jar \
company.myapp
Preparing your project for Antithesis
To send code to Antithesis, you should build as described above. Antithesis will explore your software and search for violations of the properties you have defined.
You must send us containers as described in getting started.
- The executable jar should be included in your container. In this example, the jar would contain
company/myapp.class
.
This example is simplified compared to what the full Getting Started guide describes. Note
that the method main
is the workload and times10
is the software itself.
There are no dependencies. The instrumentation symbols will go in the configuration image as stated.
Ultimately, you will receive a triage report that confirms the properties you defined are true: the output of the method is always even and the inputs to the method are sometimes odd. You now have a well-tested method that can multiply numbers by ten.