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 10x-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.

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

Recall that the basic SDK workflow is:

  1. Import the SDK classes that are being used.
  2. Use SDK classes in your code.
  3. Build your code, including the SDK as a build-time dependency.
  4. Deploy your build.

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’ll test times10 by passing it a random integer fifty times in a row. You’ve 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 quickstart 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’ll receive a triage report that confirms the properties you defined are true: the output of the function is always even and the inputs to the function are sometimes odd. You now have a well-tested function that can multiply numbers by ten, indisputably proving that you’re a 10x programmer.