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