- 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 runIdeForUiTests
.
to include test generation using Grazie in the runIdeForUiTests process, you need to pass Space username and token as properties:
gradle runIdeForUiTests -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
langwrappers
module to implement the<Language>
-specificPsiHelper
andPsiHelperProvider
. - src/: Depends on
langwrappers
because we want to usePsiHelper
and other interfaces regardless of the current language. Depends on<Language>
, to makeplugin.xml
aware of the implementations of the Extension Point.
Plugin Dependencies:
- The main
plugin.xml
file declares thepsiHelperProvider
extension point using thecom.intellij.lang.LanguageExtensionPoint
class. - 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
PsiHelperProvider
interface, we can get the appropriate<language>PsiHelper
class per file.
Implementation Details:
-
Common Module (
langwrappers
):- Contains the
PsiHelper
interface, which provides the necessary methods to interact withpsiFile
. - The
PsiHelperProvider
class includes a companion object to fetch the appropriatePsiHelper
implementation based on the file's language.
- Contains the
-
Module:
- Implements the
<Language>PsiHelper
and<Language>PsiHelperProvider
classes, 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/test
kotlin/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.
common
GenerateTestsActionClassCommon
This class contains all the logic related to generating tests for a class by all generators in the plugin.GenerateTestsActionMethodCommon
This class contains all the logic related to generating tests for a method by all generators in the plugin.GenerateTestsActionLineCommon
This class contains all the logic related to generating tests for a line by all generators in the plugin.
evosuite
GenerateTestsActionClassEvoSuite
This class contains all the logic related to generating tests for a class by EvoSuite.GenerateTestsActionMethodEvoSuite
This class contains all the logic related to generating tests for a method by EvoSuite.GenerateTestsActionLineEvoSuite
This class contains all the logic related to generating tests for a line by EvoSuite.
llm
GenerateTestsActionClassLlm
This class contains all the logic related to generating tests for a class by LLM.GenerateTestsActionMethodLlm
This class contains all the logic related to generating tests for a method by LLM.GenerateTestsActionLineLlm
This class contains all the logic related to generating tests for a line by LLM.
GenerateTestsUtils
contains useful functions forGenerateTestsActionClass
,GenerateTestsActionMethod
andGenerateTestsActionLine
classes, but one function are also used byStaticInvalidationService
.
All the classes related to the coverage visualisation can be found in coverage
directory.
CoverageRenderer
adds 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.
Workspace
handles 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.kt
edits the test suite by visiting the test cases and setting the modified body if it has been modified. It also removes scaffolding.ValidationResultListener.kt
is a topic interface for sending and receiving results of test validation.ValidationToolWindowFactory.kt
creates the tabs and the UI of the tool window corresponding to dynamic test validation.Validator.kt
validates and calculates the coverage of an optionally edited set of test cases.
All the helper classes can be found in helpers
directory.
MethodDescriptorHelper
contains helper functions for generating method descriptors. It is used byGenerateTestsActionMethod
class.
All the listener classes can be found in listeners
directory.
TestGenerationResultListenerImpl
is the implementation ofTestGenerationResultListener
topic interface. It notifiesWorkspace
of the received test generation result and also puts the generated tests into the cache.TelemetrySubmitListenerImpl
schedules 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.
CoverageSelectionToggleListener
is a topic interface for showing or hiding highlighting when the coverage is toggled for one test or many tests.CoverageToolWindowDisplayService
creates the "Coverage visualisation" tool window panel and the coverage table to display the test coverage data of the tests generated by EvoSuite.CoverageVisualisationService
visualises the coverage in the gutter and the editor (by colouring), injects the coverage data into the "Coverage visualisation" tool window tab.ErrorMonitor
class for handling error occurrences.QuickAccessParametersService
allows to load and get the state of the parameters in the "Parameters" tool window panel.RunnerService
is used to limit TestSpark to generate tests only once at a time.SettingsApplicationService
stores the application-level settings persistently. It usesSettingsApplicationState
class for that.SettingsProjectService
stores the project-level settings persistently. It usesSettingsProjectState
class for that.StaticInvalidationService
invalidates the cache statically.TestCaseCachingService
contains the data structure for caching the generated test cases and is responsible for adding, retrieving and removing (invalidating) the generated tests.TestCaseDisplayService
displays the tests generated by EvoSuite, in the "Generated tests" tool window panel.TestSparkTelemetryService
sends usage information to Intelligent Collaboration Tools Lab at JetBrains Research if the user has opted in.
All the classes related to TestSpark Settings/Preferences
page can be found in settings
directory.
SettingsApplicationState
is responsible for storing the values of the EvoSuite Settings entries.SettingsEvoSuiteComponent
displays and captures the changes to the values of the entries in the EvoSuite page of the Settings dialog.SettingsEvoSuiteConfigurable
allows to configure some EvoSuite settings via the EvoSuite page in the Settings dialog, observes the changes and manages the UI and state.SettingsPluginComponent
displays and captures the changes to the values of the entries in the TestSpark main page of the Settings dialog.SettingsPluginConfigurable
allows to configure some Plugin settings via the Plugin page in the Settings dialog, observes the changes and manages the UI and state.SettingsProjectState
is responsible for storing the values of the Plugin Settings entries.
-
evosuite
all the classes that interact with EvoSuite can be found in this directory.error
EvoSuiteErrorManager
this class represents the error manager for EvoSuite. It provides methods for handling and displaying errors and warnings encountered during EvoSuite execution.
generation
EvoSuiteProcessManager
This class manages the execution of EvoSuite, a test generation tool.ResultWatcher
listens for the results of the generation process by EvoSuite. Used in conjunction withRunner
.
EvoSuite
SettingsArguments
is used for constructing the arguments and properties for EvoSuite.
-
llm
all the classes that interact with Llm can be found in this directory.error
LLMErrorManager
LLMErrorManager is a class that handles error and warning messages for LLM (Live Logic Monitor).
generation
LLMProcessManager
LLMProcessManager is a class that implements the ProcessManager interface and is responsible for generating tests using the LLM tool.LLMRequestManager
this class represents a manager for making requests to the LLM (Large Language Model).PromptManager
a class that manages prompts for generating unit tests.TestCoverageCollector
responsible for collecting test coverage data and generating a reportTestsAssembler
assembler class for generating and organizing test cases.
Llm
the Llm class represents a tool called "Llm" that is used to generate tests for Java code.SettingsArguments
is used for constructing the arguments and properties for Llm.
-
Manager
Provides methods for generating tests using different tools. -
Pipeline
pipeline class represents a pipeline for running the test generation process. -
ProjectBuilder
builds the project before running EvoSuite and before validating the tests. -
TestGenerationResultListener
is a topic interface for sending and receiving test results produced by EvoSuite test generation process. -
ToolUtils
utils for working with tools
All the classes related to TestSpark Tool Window (on the right side) can be found in toolwindow
directory.
QuickAccessParameters
stores the main panel and the UI of the "Parameters" tool window tab.QuickAccessParametersState
is responsible for persisting the values of the parameters in the "Parameters" tool window tab.TestSparkToolWindowFactory
creates the tabs and the UI of the TestSpark tool window.
TestSparkBundle
is used to load EvoSuite messages in the code (frommessages.TestSpark
file in therecourses
directory).TestSparkDefaultsBundle
is used to load the default values of the parameters in the code (fromdefaults/TestSpark.properties
file in theresources
directory).TestSparkLabelsBundle
is used to load the text of various UI labels in the code (fromdefaults/Labels.properties
file in therecourses
directory).TestSparkToolTipsBundle
is used to load the text of various tooltips in the code (fromdefaults/Tooltips.properties
file in theresources
directory).
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.
resources
directory contains dummy projects used for testing the plugin.kotlin/org/jetbrains/research/testspark
directory contains the actual tests.helpers
directory contains tests for the method descriptor helper (MethodDescriptorHelperTest
).runner
directory contains tests for the settings arguments that are used when running EvoSuite (SettingsArgumentTest
).services
directory contains tests for the coverage visualisation and caching service classes (CoverageVisualisationServiceTest
,TestCaseCachingServicePropertyBasedTest
,TestCaseCachingServiceTest
).settings
directory contains unit tests for plugin and EvoSuite settings (SettingsEvoSuiteConfigurableTest
,SettingsPluginConfigurableTest
,TestSparkSettingsStateTest
).toolwindow
directory contains unit tests for tool window tabs (QuickAccessParametersStateTest
).uiTest
directory contains the UI tests.customfixtures
directory contains the custom fixtures that had to be created for testing.pages
directory has the frames and fixtures that are used for UI testing (IdeaFrame
,QuickAccessParametersFixtures
,SettingsFrame
,WelcomeFrame
).utils
directory contains utility files that are helpful for UI testing (RemoteRobotExtension
,StepsLogger
).tests
directory contains the actual UI tests.CoverageVisualisationToolWindowTest
contains the UI tests for Coverage Visualisation tab in the tool window.PsiSelectionTest
contains the UI tests for PSI element selection logic when generating tests for class, method and line.QuickAccessParametersTest
contains the UI tests for Quick Access Parameters tab in the tool window.SettingsComponentTest
contains the UI tests for the Settings page of the plugin (both the plugin settings page and EvoSuite settings page).