- Description
- Build project
- Run IDE for UI tests
- Plugin Configuration
- Language Support Documentation
- Classes
- Tests
In this document you can find the overall structure of TestSpark plugin. The classes are listed and their purpose is described. This section is intended for developers and contributors to TestSpark plugin.
Run gradle buildPlugin.
to include test generation using Grazie in the build process, you need to pass Space username and token as properties:
gradle buildPlugin -Dspace.username=<USERNAME> -Dspace.pass=<TOKEN>.
<TOKEN> is generated by Space, which has access to Automatically generating unit tests maven packages.
In IDEA, run the Run IDE for UI Tests configuration. Alternatively, use the following command:
gradle runIde.
to include test generation using Grazie in the runIdeForUiTests process, you need to pass Space username and token as properties:
gradle runIde -Dspace.username=<USERNAME> -Dspace.pass=<TOKEN>.
<TOKEN> is generated by Space, which has access to Automatically generating unit tests maven packages.
The TestSpark plugin supports automatic test generation for various programming languages (currently Java and Kotlin) and aims to support even more programming languages in the future.
This document provides an overview of the existing implementation of Kotlin and Java support and guidelines for adding more programming languages.
How can I add support for a new programming language? In brief, you need to extend all the necessary interfaces with implementations specific to the new language. Below, you will find a detailed guide divided into six key components of the entire pipeline with the most important interfaces addressing this goal.
The first step is to enable the collection of the appropriate information for the code under test. This part is responsible for working with the PSI (Program Structure Interface) generated by IntelliJ IDEA. It helps parse the part where the cursor is located, provides a choice of the code elements that are available for testing at cursor's position. Then find all the needed dependencies to make the prompt complete with all the necessary knowledge about the code under test.
This part is the most granular but complex at the same time.
The main reason for this is to include dependencies only for the languages we need. This avoids errors if the user does not have some languages that our plugin supports. For example, if we work with a Python project, we don't want to depend on Kotlin because it will cause an error if Kotlin isn't present.
Additionally, we want to incrementally add dependencies on other languages for faster startup. For example, we do not want to fetch the dependency on Java when we work with TypeScript. Other benefits include better organization, easier maintenance, and clearer separation of concerns. As a side-bonus, the addition of new languages will be easier.
Module Dependencies:
- langwrappers: This is a foundational module for language extensions.
- : Depends on the
langwrappersmodule to implement the<Language>-specificPsiHelperandPsiHelperProvider. - src/: Depends on
langwrappersbecause we want to usePsiHelperand other interfaces regardless of the current language. Depends on<Language>, to makeplugin.xmlaware of the implementations of the Extension Point.
Plugin Dependencies:
- The main
plugin.xmlfile declares thepsiHelperProviderextension point using thecom.intellij.lang.LanguageExtensionPointclass. - The language-specific modules extend this extension point to register their implementations.
- When the project is opened, we load the EPs needed to work with the current project. Then, using
the
PsiHelperProviderinterface, we can get the appropriate<language>PsiHelperclass per file.
Implementation Details:
-
Common Module (
langwrappers):- Contains the
PsiHelperinterface, which provides the necessary methods to interact withpsiFile. - The
PsiHelperProviderclass includes a companion object to fetch the appropriatePsiHelperimplementation based on the file's language.
- Contains the
-
Module:
- Implements the
<Language>PsiHelperand<Language>PsiHelperProviderclasses, which provide -specific logic. - Declares the extension point in
testspark-<Language>.xml.
- Implements the
To add new languages, create a separate module for this language and register its implementation as an extension of
the psiHelperProvider EP. Then follow the template provided above.
When we know how to parse the code, we need to construct the prompt.
For each language, adjust the prompt that goes to the LLM. Ensure that the language, framework platform, and mocking framework are defined correctly in:
data class PromptConfiguration(
val desiredLanguage: String,
val desiredTestingPlatform: String,
val desiredMockingFramework: String,
)Additionally, check that all the dependencies (collected by PsiHelper for the current strategy) are passed
properly. PromptGenerator and PromptBuilder are responsible for this job.
When the LLM response to our prompt is received, we have to parse it.
We want to retrieve test case, all the test functions and additional information like imports or supporting functions from the response.
The current structure of this part is located in:
kotlin/org/jetbrains/research/testspark/core/testkotlin/org/jetbrains/research/testspark/tools
It can be more easily understood with the following diagram:

TestsAssembler: Assembler class for generating and organizing test cases from the LLM response.TestSuiteParser: Extracts test cases from raw text and generates a test suite.TestBodyPrinter: Generates the body of a test function as a string.
Before showing the code to the user, it should be checked for compilation.
TestCompiler: Compiles a list of test cases and returns the compilation result.
Here one should specify the appropriate compilation strategy for each language. With all the dependencies and build paths.
Once the code generated by the LLM is checked for the compilation, it should be presented in the UI.
TestCaseDisplayService: Service responsible for the representation of all the UI components.TestSuiteView: Interface specific for working with buttons.TestClassCodeAnalyzer: Interface for retrieving information from test class code.TestClassCodeGenerator: Interface for generating and formatting test class code.
We should be able to run all the tests in the UI and then save them to the desired folder.
TestPersistentStorage: Interface representing a contract for saving generated tests to a specified file system location.
For Kotlin and Java, the TestProcessor implementation also allows saving the JaCoCo report to see the code coverage of
the test that will be saved.
The plugin configuration file is plugin.xml which can be found in src/main/resources/META-INF directory. All declarations (such as actions, services, listeners) are present in this file.
All the classes can be found in src/main/kotlin/org/jetbrains/research/testspark directory.
All the action classes can be found in actions directory.
commonGenerateTestsActionClassCommonThis class contains all the logic related to generating tests for a class by all generators in the plugin.GenerateTestsActionMethodCommonThis class contains all the logic related to generating tests for a method by all generators in the plugin.GenerateTestsActionLineCommonThis class contains all the logic related to generating tests for a line by all generators in the plugin.
evosuiteGenerateTestsActionClassEvoSuiteThis class contains all the logic related to generating tests for a class by EvoSuite.GenerateTestsActionMethodEvoSuiteThis class contains all the logic related to generating tests for a method by EvoSuite.GenerateTestsActionLineEvoSuiteThis class contains all the logic related to generating tests for a line by EvoSuite.
llmGenerateTestsActionClassLlmThis class contains all the logic related to generating tests for a class by LLM.GenerateTestsActionMethodLlmThis class contains all the logic related to generating tests for a method by LLM.GenerateTestsActionLineLlmThis class contains all the logic related to generating tests for a line by LLM.
GenerateTestsUtilscontains useful functions forGenerateTestsActionClass,GenerateTestsActionMethodandGenerateTestsActionLineclasses, but one function are also used byStaticInvalidationService.
All the classes related to the coverage visualisation can be found in coverage directory.
CoverageRendereradds extra functionality to the default line marker and gutter which is used for the coverage visualisation. It adds tooltips that, when clicking on the gutter, show which tests cover a certain line or mutants (if any) that have been introduced on that line. It also highlights the corresponding tests in the tool window tab.
All the editor classes can be found in editor directory.
Workspacehandles user workspace state and modifications of that state related to test generation. It also sets the event listeners that are triggered whenever the user changes the contents of the editor.
All the classes related to dynamic cache invalidation can be found in evosuite/validation directory.
TestCaseEditor.ktedits the test suite by visiting the test cases and setting the modified body if it has been modified. It also removes scaffolding.ValidationResultListener.ktis a topic interface for sending and receiving results of test validation.ValidationToolWindowFactory.ktcreates the tabs and the UI of the tool window corresponding to dynamic test validation.Validator.ktvalidates and calculates the coverage of an optionally edited set of test cases.
All the helper classes can be found in helpers directory.
MethodDescriptorHelpercontains helper functions for generating method descriptors. It is used byGenerateTestsActionMethodclass.
All the listener classes can be found in listeners directory.
TestGenerationResultListenerImplis the implementation ofTestGenerationResultListenertopic interface. It notifiesWorkspaceof the received test generation result and also puts the generated tests into the cache.TelemetrySubmitListenerImplschedules potential submissions of the generated telemetry into a file, which is done every 5 minutes and when the project is closed.
All the service classes can be found in services directory.
We currently have three services for managing EvoSuite settings (EvoSuiteSettingsService), the LLM-based approach/generation (LLMSettingsService), and general plugin settings (PluginSettingsService).
-
evosuiteall the classes that interact with EvoSuite can be found in this directory.errorEvoSuiteErrorManagerthis class represents the error manager for EvoSuite. It provides methods for handling and displaying errors and warnings encountered during EvoSuite execution.
generationEvoSuiteProcessManagerThis class manages the execution of EvoSuite, a test generation tool.ResultWatcherlistens for the results of the generation process by EvoSuite. Used in conjunction withRunner.
EvoSuiteSettingsArgumentsis used for constructing the arguments and properties for EvoSuite.
-
llmall the classes that interact with Llm can be found in this directory.errorLLMErrorManagerLLMErrorManager is a class that handles error and warning messages for LLM (Live Logic Monitor).
generationLLMProcessManagerLLMProcessManager is a class that implements the ProcessManager interface and is responsible for generating tests using the LLM tool.LLMRequestManagerthis class represents a manager for making requests to the LLM (Large Language Model).PromptManagera class that manages prompts for generating unit tests.TestCoverageCollectorresponsible for collecting test coverage data and generating a reportTestsAssemblerassembler class for generating and organizing test cases.
Llmthe Llm class represents a tool called "Llm" that is used to generate tests for Java code.SettingsArgumentsis used for constructing the arguments and properties for Llm.
-
ManagerProvides methods for generating tests using different tools. -
Pipelinepipeline class represents a pipeline for running the test generation process. -
ProjectBuilderbuilds the project before running EvoSuite and before validating the tests. -
TestGenerationResultListeneris a topic interface for sending and receiving test results produced by EvoSuite test generation process. -
ToolUtilsutils for working with tools
All the classes related to TestSpark Tool Window (on the right side) can be found in toolwindow directory.
QuickAccessParametersstores the main panel and the UI of the "Parameters" tool window tab.QuickAccessParametersStateis responsible for persisting the values of the parameters in the "Parameters" tool window tab.TestSparkToolWindowFactorycreates the tabs and the UI of the TestSpark tool window.
TestSparkBundleis used to load EvoSuite messages in the code (frommessages.TestSparkfile in therecoursesdirectory).TestSparkDefaultsBundleis used to load the default values of the parameters in the code (fromdefaults/TestSpark.propertiesfile in theresourcesdirectory).TestSparkLabelsBundleis used to load the text of various UI labels in the code (fromdefaults/Labels.propertiesfile in therecoursesdirectory).TestSparkToolTipsBundleis used to load the text of various tooltips in the code (fromdefaults/Tooltips.propertiesfile in theresourcesdirectory).
The vast majority of labels, tooltip-texts and messages are saved in their own .properties files. This is practical if you wish to translate the plugin to a different language - changing these files should translate most of the plugin elements. The only omitted texts are those which require certain properties set to them (e.g. IntelliJ's .preferredSize). Those have to be translated in the code. There also exists a .properties file for default TestSpark configurations. It is not related to linguistics, but useful if you wish to change default values of the plugin.
The tests for TestSpark can be found in src/test directory.
resourcesdirectory contains dummy projects used for testing the plugin.kotlin/org/jetbrains/research/testsparkdirectory contains the actual tests.helpersdirectory contains tests for the method descriptor helper (MethodDescriptorHelperTest).runnerdirectory contains tests for the settings arguments that are used when running EvoSuite (SettingsArgumentTest).servicesdirectory contains tests for the coverage visualisation and caching service classes (CoverageVisualisationServiceTest,TestCaseCachingServicePropertyBasedTest,TestCaseCachingServiceTest).settingsdirectory contains unit tests for plugin and EvoSuite settings (SettingsEvoSuiteConfigurableTest,SettingsPluginConfigurableTest,TestSparkSettingsStateTest).toolwindowdirectory contains unit tests for tool window tabs (QuickAccessParametersStateTest).uiTestdirectory contains the UI tests.customfixturesdirectory contains the custom fixtures that had to be created for testing.pagesdirectory has the frames and fixtures that are used for UI testing (IdeaFrame,QuickAccessParametersFixtures,SettingsFrame,WelcomeFrame).utilsdirectory contains utility files that are helpful for UI testing (RemoteRobotExtension,StepsLogger).testsdirectory contains the actual UI tests.CoverageVisualisationToolWindowTestcontains the UI tests for Coverage Visualisation tab in the tool window.PsiSelectionTestcontains the UI tests for PSI element selection logic when generating tests for class, method and line.QuickAccessParametersTestcontains the UI tests for Quick Access Parameters tab in the tool window.SettingsComponentTestcontains the UI tests for the Settings page of the plugin (both the plugin settings page and EvoSuite settings page).