-
Notifications
You must be signed in to change notification settings - Fork 6
Tutorial: rick
First, build pankti
cd pankti/
mvn clean install
For these tutorials, we work with an open-source Java project called Shopizer. Let's start by cloning it.
git clone git@github.com:shopizer-ecommerce/shopizer.git --branch v3.1.0
You can build and run it, as well as the react frontends, following the steps in its README. Play around with the website to get familiar with it.
Glowroot is an open-source APM agent. We use it for instrumentation and monitoring. Follow the instructions on the website to download it. Also, create a directory called plugins
within glowroot/
.
You can try to attach glowroot as a javaagent
to Shopizer, by updating its POM and then running it.
...
<properties>
<coverage.lines>.04</coverage.lines>
<coverage.branches>.01</coverage.branches>
<commons-rng-simple.version>1.3</commons-rng-simple.version>
+ <glowroot.plugin.jar>-javaagent:/path/to/glowroot/glowroot.jar</glowroot.plugin.jar>
+ <boot.jvm.args>${glowroot.plugin.jar}</boot.jvm.args>
</properties>
...
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
+ <configuration>
+ <jvmArguments>${boot.jvm.args}</jvmArguments>
+ </configuration>
</plugin>
</plugins>
<finalName>shopizer</finalName>
</build>
...
Next, we statically analyze the Shopizer source to find public methods that can be our targets for test generation.
cd pankti-extract/
java -jar target/pankti-extract-<version>-jar-with-dependencies.jar /path/to/shopizer/ --void --report
This produces ./extracted-methods-shopizer.csv
The flag void
indicates that we want to include methods that return void (i.e., don't return anything).
Next, we instrument these methods so that the object they are called on (receiving object), the objects passed as parameters, as well as the object returned from this method, are serialized as Shopizer executes. First,
cd pankti-instrument/
Since the ./extracted-methods-shopizer.csv
we created in Step 2 has a lot of methods, we can find a list of classes with the most methods with the following:
python3 find-cuts.py ../pankti-extract/extracted-methods-shopizer.csv
This creates two files: ./cuts.txt
and ./cuts-mockables.txt
.
We'll use cuts-mockables.txt
for test generation with rick. It has a list of classes, as well the number of target methods within them.
Let's pick com.salesmanager.core.business.utils.ProductPriceUtils
which has 3 potential target methods that we can instrument.
python3 instrument-mock.py ../pankti-extract/extracted-methods-shopizer.csv com.salesmanager.core.business.utils.ProductPriceUtils
This generates 6 aspect classes (MethodAspect1, MethodAspect1Nested1
to MethodAspect3, MethodAspect3Nested1
) in se.kth.castor.pankti.instrument.plugins
and also updates pankti-instrument/src/main/resources/META-INF/glowroot.plugin.json
with this list of new aspects. We can finally package this into a plugin with
mvn clean install
This produces pankti-instrument/target/pankti-instrument-<version>-jar-with-dependencies.jar
. Copy this jar and paste it in the glowroot/plugins/
directory we created in Step 1.
Now that we've added the plugin with the serialization instructions to glowroot, run Shopizer (make sure you've modified the Shopizer POM as presented in Step 1). Click around the website and interact with the products, etc. You'll find that /tmp/pankti-object-data/
has some XML files (which are the serialized versions of objects) and an invoked-methods.csv
(which is a list of the methods that were invoked within com.salesmanager.core.business.utils.ProductPriceUtils
as a result of your interactions). You can stop the Shopizer server when you're done interacting with the site.
Finally,
cd rick/
java -jar target/rick-<version>-jar-with-dependencies.jar /path/to/shopizer/ /tmp/pankti-object-data/invoked-methods.csv /tmp/pankti-object-data/
This generates tests within rick/output/generated/shopizer/
as well as related resource files for longer object XMLs.
Find the list of generated test classes with
cd output/generated/shopizer/
find . -name "Test*PanktiGen"
We see that we've generated the test class TestProductPriceUtilsPanktiGen.java
. The generated resource files are in rick/output/generated/object-data/
.
Create a new project module within Shopizer called rick-tests
, add the following properties and dependencies in its POM.
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<junit.jupiter.engine.version>5.7.0</junit.jupiter.engine.version>
<junit.platform.runner.version>1.7.0</junit.platform.runner.version>
<junit.jupiter.params.version>5.7.0</junit.jupiter.params.version>
<xstream.version>1.4.12</xstream.version>
<mockito.core.version>4.4.0</mockito.core.version>
<mockito.junit.jupiter.version>2.23.0</mockito.junit.jupiter.version>
</properties>
<dependencies>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>${xstream.version}</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.engine.version}</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>${junit.platform.runner.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.params.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.engine.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.engine.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.core.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.junit.jupiter.version}</version>
</dependency>
<dependency>
<groupId>com.shopizer</groupId>
<artifactId>sm-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
...
Create the package com.salesmanager.core.business.utils
under src/test/java/
within this new module. Copy the generated test inside this package. Also create src/test/resources/
and add the generated resource files. The generated tests are now ready to run!
Note: Deserialization for java.sql.Date
and java.sql.Timestamp
fails, causing the tests to fail. Here's a fix, to be added to the generated test:
@BeforeAll
public static void setxStream() {
xStream.registerConverter(new Converter() {
@Override
public void marshal(Object o, HierarchicalStreamWriter hierarchicalStreamWriter, MarshallingContext marshallingContext) {}
@Override
public Object unmarshal(HierarchicalStreamReader hierarchicalStreamReader, UnmarshallingContext unmarshallingContext) {
return new Timestamp(System.currentTimeMillis());
}
@Override
public boolean canConvert(Class aClass) {
return aClass.getCanonicalName().equals("java.sql.Timestamp") ||
aClass.getCanonicalName().equals("java.sql.Date");
}
});
}