-
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");
}
});
}