Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions .github/instructions/pytest-json-test-builder.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
applyTo: 'python_files/tests/pytestadapter/test_discovery.py'
description: 'A guide for adding new tests for pytest discovery and JSON formatting in the test_pytest_collect suite.'
---

# How to Add New Pytest Discovery Tests

This guide explains how to add new tests for pytest discovery and JSON formatting in the `test_pytest_collect` suite. Follow these steps to ensure your tests are consistent and correct.

---

## 1. Add Your Test File

- Place your new test file/files in the appropriate subfolder under:
```
python_files/tests/pytestadapter/.data/
```
- Organize folders and files to match the structure you want to test. For example, to test nested folders, create the corresponding directory structure.
- In your test file, mark each test function with a comment:
```python
def test_function(): # test_marker--test_function
...
```

**Root Node Matching:**

- The root node in your expected output must match the folder or file you pass to pytest discovery. For example, if you run discovery on a subfolder, the root `"name"`, `"path"`, and `"id_"` in your expected output should be that subfolder, not the parent `.data` folder.
- Only use `.data` as the root if you are running discovery on the entire `.data` folder.

**Example:**
If you run:

```python
helpers.runner([os.fspath(TEST_DATA_PATH / "myfolder"), "--collect-only"])
```

then your expected output root should be:

```python
{
"name": "myfolder",
"path": os.fspath(TEST_DATA_PATH / "myfolder"),
"type_": "folder",
...
}
```

---

## 2. Update `expected_discovery_test_output.py`

- Open `expected_discovery_test_output.py` in the same test suite.
- Add a new expected output dictionary for your test file, following the format of existing entries.
- Use the helper functions and path conventions:
- Use `os.fspath()` for all paths.
- Use `find_test_line_number("function_name", file_path)` for the `lineno` field.
- Use `get_absolute_test_id("relative_path::function_name", file_path)` for `id_` and `runID`.
- Always use current path concatenation (e.g., `TEST_DATA_PATH / "your_folder" / "your_file.py"`).
- Create new constants as needed to keep the code clean and maintainable.

**Important:**

- Do **not** read the entire `expected_discovery_test_output.py` file if you only need to add or reference a single constant. This file is very large; prefer searching for the relevant section or appending to the end.

**Example:**
If you run discovery on a subfolder:

```python
helpers.runner([os.fspath(TEST_DATA_PATH / "myfolder"), "--collect-only"])
```

then your expected output root should be:

```python
myfolder_path = TEST_DATA_PATH / "myfolder"
my_expected_output = {
"name": "myfolder",
"path": os.fspath(myfolder_path),
"type_": "folder",
...
}
```

- Add a comment above your dictionary describing the structure, as in the existing examples.

---

## 3. Add Your Test to `test_discovery.py`

- In `test_discovery.py`, add your new test as a parameterized case to the main `test_pytest_collect` function. Do **not** create a standalone test function for new discovery cases.
- Reference your new expected output constant from `expected_discovery_test_output.py`.

**Example:**

```python
@pytest.mark.parametrize(
("file", "expected_const"),
[
("myfolder", my_expected_output),
# ... other cases ...
],
)
def test_pytest_collect(file, expected_const):
...
```

---

## 4. Run and Verify

- Run the test suite to ensure your new test is discovered and passes.
- If the test fails, check your expected output dictionary for path or structure mismatches.

---

## 5. Tips

- Always use the helper functions for line numbers and IDs.
- Match the folder/file structure in `.data` to the expected JSON structure.
- Use comments to document the expected output structure for clarity.
- Ensure all `"path"` and `"id_"` fields in your expected output match exactly what pytest returns, including absolute paths and root node structure.

---

**Reference:**
See `expected_discovery_test_output.py` for more examples and formatting. Use search or jump to the end of the file to avoid reading the entire file when possible.
1 change: 1 addition & 0 deletions build/test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ pytest-describe

# for pytest-ruff related tests
pytest-ruff
pytest-black
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def add(a, b):
return a + b


def subtract(a, b):
return a - b
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest
from app import add, subtract


def test_add(): # test_marker--test_add
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0


def test_subtract(): # test_marker--test_subtract
assert subtract(5, 3) == 2
assert subtract(0, 0) == 0
assert subtract(-1, -1) == 0
99 changes: 99 additions & 0 deletions python_files/tests/pytestadapter/expected_discovery_test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -1719,3 +1719,102 @@
],
"id_": TEST_DATA_PATH_STR,
}

# This is the expected output for the 2496-black-formatter folder when run with black plugin
# └── .data
# └── 2496-black-formatter
# └── app.py
# └── black
# └── test_app.py
# └── black
# └── test_add
# └── test_subtract
black_formatter_folder_path = TEST_DATA_PATH / "2496-black-formatter"
black_app_path = black_formatter_folder_path / "app.py"
black_test_app_path = black_formatter_folder_path / "test_app.py"
black_formatter_expected_output = {
"name": "2496-black-formatter",
"path": os.fspath(black_formatter_folder_path),
"type_": "folder",
"id_": os.fspath(black_formatter_folder_path),
"children": [
{
"name": "app.py",
"path": os.fspath(black_app_path),
"type_": "file",
"id_": os.fspath(black_app_path),
"children": [
{
"name": "black",
"path": os.fspath(black_app_path),
"lineno": "0",
"type_": "test",
"id_": get_absolute_test_id(
"2496-black-formatter/app.py::black",
black_app_path,
),
"runID": get_absolute_test_id(
"2496-black-formatter/app.py::black",
black_app_path,
),
}
],
},
{
"name": "test_app.py",
"path": os.fspath(black_test_app_path),
"type_": "file",
"id_": os.fspath(black_test_app_path),
"children": [
{
"name": "black",
"path": os.fspath(black_test_app_path),
"lineno": "0",
"type_": "test",
"id_": get_absolute_test_id(
"2496-black-formatter/test_app.py::black",
black_test_app_path,
),
"runID": get_absolute_test_id(
"2496-black-formatter/test_app.py::black",
black_test_app_path,
),
},
{
"name": "test_add",
"path": os.fspath(black_test_app_path),
"lineno": find_test_line_number(
"test_add",
black_test_app_path,
),
"type_": "test",
"id_": get_absolute_test_id(
"2496-black-formatter/test_app.py::test_add",
black_test_app_path,
),
"runID": get_absolute_test_id(
"2496-black-formatter/test_app.py::test_add",
black_test_app_path,
),
},
{
"name": "test_subtract",
"path": os.fspath(black_test_app_path),
"lineno": find_test_line_number(
"test_subtract",
black_test_app_path,
),
"type_": "test",
"id_": get_absolute_test_id(
"2496-black-formatter/test_app.py::test_subtract",
black_test_app_path,
),
"runID": get_absolute_test_id(
"2496-black-formatter/test_app.py::test_subtract",
black_test_app_path,
),
},
],
},
],
}
1 change: 1 addition & 0 deletions python_files/tests/pytestadapter/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ def test_parameterized_error_collect():
"pytest_describe_plugin" + os.path.sep + "nested_describe.py",
expected_discovery_test_output.expected_nested_describe_output,
),
("2496-black-formatter", expected_discovery_test_output.black_formatter_expected_output),
],
)
def test_pytest_collect(file, expected_const):
Expand Down
1 change: 0 additions & 1 deletion python_files/vscode_pytest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
if TYPE_CHECKING:
from pluggy import Result


USES_PYTEST_DESCRIBE = False

with contextlib.suppress(ImportError):
Expand Down
9 changes: 8 additions & 1 deletion src/client/testing/testController/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,14 @@ export function populateTestTree(

let range: Range | undefined;
if (child.lineno) {
range = new Range(new Position(Number(child.lineno) - 1, 0), new Position(Number(child.lineno), 0));
if (Number(child.lineno) === 0) {
range = new Range(new Position(0, 0), new Position(0, 0));
} else {
range = new Range(
new Position(Number(child.lineno) - 1, 0),
new Position(Number(child.lineno), 0),
);
}
}
testItem.canResolveChildren = false;
testItem.range = range;
Expand Down
Loading