Skip to main content

Test Explorer

Test Explorer is under active development, this page explain how it works under the hood.

Test Discovery

Test Explorer (TE) provides two ways to interact with it:

  • The first one is based on notifications being sent from Metals to the client and doesn't require the client to do any actions except listen for the notifications. All necessary updates are being immediately sent to it.

  • The client can also manually ask Metals for test suites for the whole workspace, or for the test cases for a given file.


In order to use TE, the client has to set InitializationOptions.testExplorerProvider and also have the user configuration setting test-user-interface set to test explorer. This action will deactivate code lenses for test classes and enable TE to send updates.

The preferred way to implement TE is to implement client's command metals-update-test-explorer. Using this request, Metals will push all necessary updates to the client.

It is also possible to depend on server command, however this option is discouraged. In the future it may be deprecated and then removed. Currently it exists because VSCode allows users to choose Test UI and this request is used whenever such change in options occurs.

How it works under the hood

Test Explorer uses BSP's debug request to run and debug test classes. TE reuses already available debug support in Metals and build servers such as Bloop or SBT. Both run and debug under the hood use BSP's debug request.

Running and debugging

Following diagram (source) represents what communication between language client, language server and build server.

The current implementation of TE is a better version of code lenses, with more user friendly UX provided by the VSCode.

Getting and analyzing results

After test run was finished TE analyzes execution results and sets proper statutes for each test included in run.

Such detailed information is available thanks to the sbt-test-interface which abstracts over test framework internals, most important classes are: Event and Status which are used in the sbt.ForkMain. Almost all of build servers (if not all of them) uses ForkMain and sbt-test-interface to offload testing.

For each test, there is an event fired which contains all necessary information about execution: fully qualified name of test, execution status and duration. These events are captured by the BSP server (which runs ForkMain on the behalf of DAP) and then forwarded to the DAP client as a custom event. Then, those events are captured by the client and used to determine tests results, after debus session is ended.

An example request forwarded to the client looks like:

"type": "event",
"seq": 20,
"event": "testResult",
"body": {
"category": "testResult",
"data": {
"suiteName": "a.JunitTestSuite",
"duration": 195,
"tests": [
"kind": "failed",
"testName": "a.JunitTestSuite.test1",
"duration": 135,
"error": "* a.JunitTestSuite.test1 - java.lang.AssertionError: assertion failed: should equal be true"
"kind": "passed",
"testName": "a.JunitTestSuite.test2",
"duration": 60
"type": "testResult"