Skip to content

Components Test Management

Karthik Nadig edited this page Jan 27, 2021 · 1 revision

The extension provides testing for Python code through one of the supported frameworks. This involves a variety of capabilities in addition to just running tests. The following frameworks are supported:

Functionality

operations

  • install selected test framework if not installed
  • test discovery
  • run all/selected tests & suites
  • debug all/selected tests & suites
  • breakpoints while running tests
  • show (high level) test results

Note that the active Python interpreter is used for all testing operations.

resources

  • (somewhat) granular test framework config settings
  • dedicated output log

commands

  • "Python: Discover Unit Tests"
  • "Python: Run All Unit Tests"
  • "Python: Run Unit Test Method..."
  • "Python: View Unit Test Output"

UI

  • status bar element
    • shows test results
    • triggers quick pick for testing-related operations
  • pop-up (& quick pick) to configure test framework
  • code lens on tests and suites:
    • run/debug
    • results
  • output panel: "Python Test Log"
  • TBD: Test Explorer activity (top-level VSC element)

config settings

  • "python.unitTest.cwd"
  • "python.unitTest.debugPort"
  • "python.unitTest.nosetestArgs"
  • "python.unitTest.nosetestsEnabled"
  • "python.unitTest.nosetestPath"
  • "python.unitTest.promptToConfigure"
  • "python.unitTest.pyTestArgs"
  • "python.unitTest.pyTestEnabled"
  • "python.unitTest.pyTestPath"
  • "python.unitTest.unittestArgs"
  • "python.unitTest.unittestEnabled"
  • "python.unitTest.autoTestDiscoverOnSaveEnabled"

Extension Code

codebase structure

All test-related code is contained entirely under a single directory, src/client/unittests/:

src/client/unittest
├── codeLenses
├── common
│ ├── managers
│ ├── services
│ └── testVisitors
├── display
├── nosetest
│ └── services
├── pytest
│ └── services
└── unittest
└── services

files: external API

src/client/unittest
├── serviceRegistry.ts
└── types.ts

files: top-level code

src/client/unittest
├── common
│ ├── argumentsHelper.ts
│ ├── constants.ts
│ ├── debugLauncher.ts
│ ├── managers
│ │ ├── baseTestManager.ts
│ │ └── testConfigurationManager.ts
│ ├── runner.ts
│ ├── services
│ │ ├── configSettingService.ts
│ │ ├── storageService.ts
│ │ ├── testManagerService.ts
│ │ ├── testResultsService.ts
│ │ ├── unitTestDiagnosticService.ts
│ │ └── workspaceTestManagerService.ts
│ ├── testUtils.ts
│ ├── testVisitors
│ │ ├── flatteningVisitor.ts
│ │ ├── folderGenerationVisitor.ts
│ │ └── resultResetVisitor.ts
│ ├── types.ts
│ └── xUnitParser.ts
└── main.ts

files: configuration:

src/client/unittest
├── configurationFactory.ts
├── configuration.ts
├── nosetest
│ └── testConfigurationManager.ts
├── pytest
│ └── testConfigurationManager.ts
└── unittest
└── testConfigurationManager.ts

files: testing frameworks:

src/client/unittest
├── nosetest
│ ├── main.ts
│ ├── runner.ts
│ └── services
│ ├── argsService.ts
│ ├── discoveryService.ts
│ └── parserService.ts
├── pytest
│ ├── main.ts
│ ├── runner.ts
│ └── services
│ ├── argsService.ts
│ ├── discoveryService.ts
│ ├── parserService.ts
│ └── testMessageService.ts
└── unittest
├── helper.ts
├── main.ts
├── runner.ts
├── services
│ ├── argsService.ts
│ ├── discoveryService.ts
│ └── parserService.ts
└── socketServer.ts

files: UI features

src/client/unittest
├── codeLenses
│ ├── main.ts
│ └── testFiles.ts
└── display
├── main.ts
└── picker.ts

components

  • config
  • test discovery
  • code lens
  • picker
  • runner

Adapter Script

  • source: pythonFiles/testing_tools/adapter/__main__.py
  • invocation:
    • [PYTHON] [SCRIPT] discover pytest [ARGS] -- [PYTEST ARGS]
    • SCRIPT (any python): pythonFiles/testing_tools/run_adapter.py
    • SCRIPT (python3-only): -m pythonFiles.testing_tools.adapter
  • args:
    • --simple - simplified output for use while debugging
    • --no-hide-stdio - do not hide pytest stdout/stderr during discover (useful when debugging)
    • --pretty - format output for readability
  • implicit pytest args:
    • --collect-only

Output

OUTPUT: ROOTED_TESTS[]
ROOTED_TESTS: {
# Uniquely identifies test root.
"rootid": str
# Absolute path to test root.
"root": str
# The discovered files, suites, etc. under this root.
"parents": PARENT[]
# The discovered tests under this root.
"tests": TEST[]
}
PARENT: {
# Uniquely identifies the parent.
"id": str
# The kind of parent.
"kind": "folder"|"file"|"suite"|"function"|"subtest"
# A human-friendly identifier.
"name": str
# The parent's parent (i.e. in a tree from the root.
"parentid": str
}
TEST: {
# Uniquely identifies the test.
"id": str
# A human-friendly identifier.
"name": str
# The location of the test's source code.
"source": str ("<FILENAME>:<LINENO>")
# Any supported markers associated with the test.
"markers": ("skip"|"skip-if"|"expected-failure")[]
# The ID of the test's parent.
"parentid": str
}

Notes:

  • IDs may be used to identify a test or parent to the test framework (e.g. to run specific tests)
  • every child of the root will have the "rootid" as its "parentid"
  • supported test markers are framework-agnostic but correspond to framework-specific ones
  • a test is considered a "subtest" if its parent is a function or a subtest
  • a subtest is a test that is effectively a parameterization of a more general test
    • in pytest parameterized tests are the only kind of subtest
  • a subtest may be a parent if there is a nested subtest in it
    • this is not supported in pytest, but is in unittest

Example:

[{
"rootid": ".",
"root": "/x/y/z",
"parents": [{
"id": "./test_spam.py",
"kind": "file",
"name": "test_spam.py",
"parentid": "."
}, {
"id": "./test_spam.py::SpamTests",
"kind": "suite",
"name": "SpamTests",
"parentid": "./test_spam.py"
},
"tests" [{
"id": "./test_spam.py::test_all",
"name": "test_all",
"source": "test_spam.py:11",
"markers": ["skip", "expected-failure"],
"parentid": "./test_spam.py"
}, {
"id": "./test_spam.py::SpamTests::test_spam1",
"name": "test_spam1",
"source": "test_spam.py:23",
"markers": ["skip"],
"parentid": "./test_spam.py::SpamTests"
}]
}]

Clone this wiki locally